Kingfisher crashes on SessionDelegate.remove()

215 views Asked by At

We are using Kingfisher to download images for our iOS app and I have encountered many crashes when trying to cancel a download task

EXC_BAD_ACCESS KERN_INVALID_ADDRESS 0x0000000000000020

Crashed: com.apple.main-thread
0  CFNetwork                      0x18436b058 HTTPParser::HTTPParser(__CFAllocator const*, HTTPParserClient*, HTTPParser*) + 44
1  CFNetwork                      0x18424b27c HTTPMessage::commonInitialization(unsigned char, HTTPMessage const*) + 212
2  CFNetwork                      0x18424b27c HTTPMessage::commonInitialization(unsigned char, HTTPMessage const*) + 212
3  CFNetwork                      0x18424b41c HTTPMessage::HTTPMessage(HTTPMessage const*) + 112
4  CFNetwork                      0x18436c1dc HTTPRequestMessage::HTTPRequestMessage(HTTPRequestMessage const*) + 36
5  CFNetwork                      0x18422a74c HTTPRequest::HTTPRequest(HTTPRequest const*) + 24
6  CFNetwork                      0x18422ce8c URLRequest::initialize(URLRequest const*, unsigned char) + 656
7  CFNetwork                      0x1841e2bd8 _createRequestCopy(__CFAllocator const*, _CFURLRequest const*, unsigned char) + 152
8  CFNetwork                      0x184355de0 -[NSURLRequest mutableCopyWithZone:] + 44
9  libswiftFoundation.dylib       0x105a95a0c URLRequest.init(_bridged:) + 77948
10 libswiftFoundation.dylib       0x105a99344 static URLRequest._unconditionallyBridgeFromObjectiveC(_:) + 92596
11 Kingfisher                     0x102fb5788 SessionDelegate.remove(_:) + 93 (SessionDelegate.swift:93)
12 Kingfisher                     0x102fb566c closure #1 in SessionDelegate.add(_:url:callback:) + 1000 (<compiler-generated>:1000)
13 Kingfisher                     0x102fb9eb0 partial apply for thunk for @escaping @callee_guaranteed (@guaranteed SessionDelegate, @unowned Int, @in_guaranteed SessionDataTask.TaskCallback) -> () + 84 (<compiler-generated>:84)
14 Kingfisher                     0x102f46734 specialized closure #1 in Delegate.delegate<A>(on:block:) + 96 (<compiler-generated>:96)
15 Kingfisher                     0x102fb9f34 partial apply for specialized closure #1 in Delegate.delegate<A>(on:block:) + 28 (<compiler-generated>:28)
16 Kingfisher                     0x102fb14d0 SessionDataTask.cancel(token:) + 356 (<compiler-generated>:356)
17 Kingfisher                     0x102f6de18 DownloadTask.cancel() + 74 (ImageDownloader.swift:74)
18 MyApp                          0x1011e55f4 UIImageView.cancel() + 4310799860 (<compiler-generated>:4310799860)
...
...

For example we call this method when reusing cells in a tableview, where we cancel requests for cells that are not longer visible. We have created an extension on UIImageView to define convenience methods like cancel()

  • What could be the reason for this crash? The download task in our code is an optional, so even when the object is not there anymore, calling image?.downloadTask.cancel() should not create crashes, should not even trigger those methods.

In some cases, I have seem that the app crashes when the HTTP Request have been cancelled by the server, but is not the only time it happens.

Here is a Sample code of what we do:

func fetchImage(fromURL url: URL, completion: @escaping (UIImage?, NSError?) -> Void) -> MyAppDownloadTask {    
    
    let resource = ImageResource(downloadURL: url)
    let options: KingfisherOptionsInfo = [.targetCache(cache), .downloader(downloader)]

    let task = KingfisherManager.shared.retrieveImage(with: resource, options: options, progressBlock: nil) { result in

    switch result {
        case .success(let value):
            completion(value.image, nil)
        case .failure(let error):
            completion(nil, error.toNSError())
    }
    }

    return MyAppDownloadTask(task: task)
}

private class MyAppDownloadTask {

    let task: DownloadTask? // KingFisher task

    init(task: DownloadTask?) {
        self.task = task
    }

    func cancel() {
        task?.cancel()
    }
}

We call the cancel method when reusing cells in our collection view:

override func prepareForReuse() {
    super.prepareForReuse()
    downloadTask.cancel()
}
0

There are 0 answers