SDWebImage / SDWebImage

Asynchronous image downloader with cache support as a UIImageView category
https://sdwebimage.github.io
MIT License
25.02k stars 5.96k forks source link

Crash in swift, when I use self.xxxx in the completed block #833

Closed Horse888 closed 9 years ago

Horse888 commented 10 years ago
self.imageView.sd_setImageWithURL(MYIMAGEURL
            , placeholderImage: nil
            , options: SDWebImageOptions.RetryFailed
            , progress: {(receivedSize: Int!, expectedSize: Int!) in

            }
            , completed:{(image: UIImage?, error: NSError?, cacheType: SDImageCacheType!, imageURL: NSURL?) in
                if image {
                    UIView.transitionWithView(self.imageView
                        , duration: 0.3
                        , options: UIViewAnimationOptions.AllowUserInteraction | UIViewAnimationOptions.TransitionCrossDissolve
                        , animations: {

                        }
                        , completion: {(finished) in

                        })
                }
            })
bpoplauschi commented 10 years ago

Could you provide the stack trace?

Horse888 commented 10 years ago
thread #1: tid = 0x7fdda6, 0x03310e13 libobjc.A.dylib`objc_retain + 19, 
queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS 
(code=1, address=0x65726566)

        self.imageView.sd_setImageWithURL(MYIMAGEURL
            , placeholderImage: nil
            , options: SDWebImageOptions.RetryFailed
            , completed:{(image: UIImage?, error: NSError?, cacheType: SDImageCacheType!, imageURL: NSURL?) in
                if image != nil {
                    println(self)
            })
bpoplauschi commented 10 years ago

Please take a look at https://github.com/rs/SDWebImage/issues/414, this a stack trace info we would need to be able to fix the issue. A demo project to demo the crash would be even more useful.

Horse888 commented 10 years ago
import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var imageView: UIImageView!
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        let block: SDWebImageCompletionBlock = {[unowned self](image: UIImage!, error: NSError!, cacheType: SDImageCacheType!, imageURL: NSURL!) -> Void in
            println(self)
        }

        var url = NSURL(string: "http://yikaobang-test.u.qiniudn.com/FnZTPYbldNXZi7cQ5EJHmKkRDTkj")
        self.imageView.sd_setImageWithURL(url, completed:block)
    }
}

Crash at line println(self)

change to below code without luck either:

let block: SDWebImageCompletionBlock = {(image: UIImage!, error: NSError!, cacheType: SDImageCacheType!, imageURL: NSURL!) -> Void in
            println(self)
        }

The self is released before I want to use in the block.

 thread backtrace
* thread #1: tid = 0xaf7d9, 0x0020d7c1 libswiftCore.dylib`swift_getObjectType + 17, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x10)
    frame #0: 0x0020d7c1 libswiftCore.dylib`swift_getObjectType + 17
    frame #1: 0x001e9016 libswiftCore.dylib`swift_stdlib_dynamicCastToExistential1 + 230
    frame #2: 0x000ce05e libswiftCore.dylib`Swift.print <A, B : Swift.OutputStreamType>(A, inout B) -> () + 142
    frame #3: 0x001463d8 libswiftCore.dylib`Swift.println <A>(A) -> () + 88
  * frame #4: 0x0003bb30 testSD`testSD.ViewController.(image=Some, error=None, cacheType=Some, imageURL=Some, self=0x00000000) -> () -> ()).(closure #1) + 112 at ViewController.swift:18
    frame #5: 0x0003b106 testSD`partial apply forwarder for testSD.ViewController.(viewDidLoad (testSD.ViewController) -> () -> ()).(closure #1) + 118 at ViewController.swift:0
    frame #6: 0x0003bc0b testSD`reabstraction thunk helper from @callee_owned (@owned Swift.ImplicitlyUnwrappedOptional<ObjectiveC.UIImage>, @owned Swift.ImplicitlyUnwrappedOptional<ObjectiveC.NSError>, @unowned C.SDImageCacheType, @owned Swift.ImplicitlyUnwrappedOptional<ObjectiveC.NSURL>) -> (@unowned ()) to @callee_unowned @objc_block (@unowned Swift.ImplicitlyUnwrappedOptional<ObjectiveC.UIImage>, @unowned Swift.ImplicitlyUnwrappedOptional<ObjectiveC.NSError>, @unowned C.SDImageCacheType, @unowned Swift.ImplicitlyUnwrappedOptional<ObjectiveC.NSURL>) -> (@unowned ()) + 155 at ViewController.swift:17
    frame #7: 0x0003e201 testSD`__88-[UIImageView(.block_descriptor=<unavailable>) sd_setImageWithURL:placeholderImage:options:progress:completed:]_block_invoke_2 + 449 at UIImageView+WebCache.m:53
    frame #8: 0x0003dc92 testSD`__88-[UIImageView(.block_descriptor=<unavailable>, image=0x7be6c630, error=0x00000000, cacheType=SDImageCacheTypeDisk, finished='\x01', imageURL=0x7be6dc10) sd_setImageWithURL:placeholderImage:options:progress:completed:]_block_invoke + 626 at UIImageView+WebCache.m:53
    frame #9: 0x00052c33 testSD`__69-[SDWebImageManager downloadImageWithURL:options:progress:completed:]_block_invoke149(.block_descriptor=<unavailable>) + 179 at SDWebImageManager.m:252
    frame #10: 0x0004f8c8 testSD`__69-[SDWebImageManager downloadImageWithURL:options:progress:completed:]_block_invoke76(.block_descriptor=<unavailable>, image=0x7be6c630, cacheType=SDImageCacheTypeDisk) + 3448 at SDWebImageManager.m:252
    frame #11: 0x00062274 testSD`__42-[SDImageCache queryDiskCacheForKey:done:]_block_invoke_2(.block_descriptor=<unavailable>) + 52 at SDImageCache.m:310
    frame #12: 0x023cf7b8 libdispatch.dylib`_dispatch_call_block_and_release + 15
    frame #13: 0x023e44d0 libdispatch.dylib`_dispatch_client_callout + 14
    frame #14: 0x023d2726 libdispatch.dylib`_dispatch_main_queue_callback_4CF + 340
    frame #15: 0x00a7e43e CoreFoundation`__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 14
    frame #16: 0x009bf5cb CoreFoundation`__CFRunLoopRun + 1963
    frame #17: 0x009be9d3 CoreFoundation`CFRunLoopRunSpecific + 467
    frame #18: 0x009be7eb CoreFoundation`CFRunLoopRunInMode + 123
    frame #19: 0x033f85ee GraphicsServices`GSEventRunModal + 192
    frame #20: 0x033f842b GraphicsServices`GSEventRun + 104
    frame #21: 0x011ccf9b UIKit`UIApplicationMain + 1225
    frame #22: 0x0003c7fe testSD`top_level_code + 78 at AppDelegate.swift:12
    frame #23: 0x0003c83b testSD`main + 43 at AppDelegate.swift:0
    frame #24: 0x026386d9 libdyld.dylib`start + 1
(lldb) 
bpoplauschi commented 10 years ago

@ZuYuanZhou please create a demo project which I can download and try, so I can reproduce the crash and try to fix it. Thanks :)

lubert commented 10 years ago

I'm experiencing this issue too, I'll try to make a demo project this weekend unless @ZuYuanZhou beats me to it

Horse888 commented 10 years ago

Thanks @lubert, @bpoplauschi , I have created the demo project which you can find here: https://github.com/ZuYuanZhou/testSD.git

I test it with Xcode6-Beta5, swift

mk3d commented 10 years ago

Same issue for me! Can't use self in completion block. If i try to println it ok, but any call with self crash with EXC_BAD_ACCESS Any idea? Many thanks

lubert commented 10 years ago

A possible workaround is to use SDWebImageDownloader instead.

SDWebImageDownloader.sharedDownloader().downloadImageWithURL(imageUrl, options: nil, progress: nil, completed: {[weak self] (image, data, error, finished) in
    if let wSelf = self {
        // do what you want with the image/self
    }
})
mk3d commented 10 years ago

Thanks @lubert, thats it! Many thanks for this workaround. Just have to call like this

    SDWebImageDownloader.sharedDownloader().downloadImageWithURL(myImageUrl, options: nil, progress: nil, completed: {[weak self] (image, data, error, finished) in
        if let wSelf = self {
            // do what you want with the image/self
            println("have an image now")
            self?.imageView.image = image
        }
    })
kristoforsalmin commented 10 years ago

Thanks @lubert, @mk3d for solution with SDWebImageDownloader. How I do use SDImageCacheType and SDWebImageDownloader together? I need something like that:

[cell.actionImage setImageWithURL:[NSURL URLWithString:[cellData valueForKey:@"image_path"]]
                     placeholderImage:nil
                            completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) {
                                if (image && cacheType != SDImageCacheTypeMemory) {
                                    [self fadeInLayer:cell.actionImage.layer];
                                }
                            }];
james075 commented 10 years ago

Another workaround is by using:

    self.imageView.sd_setImageWithURL(imageURL)

If you don't have any image manipulation

kristoforsalmin commented 10 years ago

@james075 i know about this method. But I need image and cache manipulation in completion block.

vinod1988 commented 10 years ago

Can u tried a progress comletion method . Just tried it hope it work well.

On Fri, Sep 5, 2014 at 10:55 AM, Kristofor Salmin notifications@github.com wrote:

@james075 https://github.com/james075 i know about this method. But I need image manipulation in completion block.

— Reply to this email directly or view it on GitHub https://github.com/rs/SDWebImage/issues/833#issuecomment-54586339.

Thanks & Regards Sr Software Engineer | Vinod Vishwakarma Skype: vinod.vishwakarma8 M:7208317956

kristoforsalmin commented 10 years ago

@vinod1988 thanks for help. Progress method works fine. But completion block not works for me. Any idea? Thanks so much.

cell.thumbnail.sd_setImageWithURL(NSURL(string: cellData["image_path"].string!), placeholderImage: UIImage(), options: SDWebImageOptions.RefreshCached,
                progress: { (receivedSize: Int, expectedSize: Int) -> Void in
                    cell.thumbnail.layer.fadeIn() // Working!
                },
                completed: { (image: UIImage!, error: NSError!, cacheType: SDImageCacheType!, imageURL: NSURL!) -> Void in
                    if cacheType != SDImageCacheType.Memory {
                        cell.thumbnail.layer.fadeIn() // Crash: Thread 1: EXC_BAD_ACCESS (code=2, address=0x30)
                    }
                })
mk3d commented 10 years ago

I have used like this with cache in swift: Hope this help :)

// Get the image form cache or download it
SDWebImageManager.sharedManager().downloadImageWithURL(NSURL(string: sUrl),
    options: nil,
    progress: nil,
    completed: {[weak self] (image, error, cached, finished, url) in
        if let wSelf = self {
            println("i have image now")

            // do what you want with the image/self
            myImageView.image = image
        }
})
kristoforsalmin commented 10 years ago

@mk3d how I do determine if image loaded from cache or image downloaded? Like a:

if cacheType != SDImageCacheType.Memory

Thanks!

sean-hill commented 9 years ago

I'm having this same issue as mentioned here.

netwire88 commented 9 years ago

Yes, same here.. unforthnately.

masterkrang commented 9 years ago

Any updates on this? Having the same issue.

jaernouts commented 9 years ago

Any updates? I see this issue still exists... Is there another workaround? I should have caching enabled, and the SDWebImageDownloader workaround doesn't use caching....

harshcs commented 9 years ago

I am not able to use SDWebImage in my Swift project! Please let me know how can I use that? I want to use "- (void)sd_setImageWithURL:(NSURL *)url;" in my Swift file. But I am not able to use that as compiler is not recognizing this function and giving me error! Please help! Its been a month I am stuck on this! Any sample code will be helpful!

bpoplauschi commented 9 years ago

@harshcs you don't need to post this question in so many places, it creates confusion. Please look at this threads' comments and see how people managed to make their Swift code work with our library.

harshcs commented 9 years ago

I know this but I am not getting any response from anywhere and I am stuck since long time. Can you please help me in this regard! I have also opened an issue regarding the same but still no response on that.

bpoplauschi commented 9 years ago

Please set up a demo project to show the issue you are encountering.

harshcs commented 9 years ago

Its simple to understand the issue: I have followed the steps for integrating the SDWebImage and It worked for me in with Objective C but now I have to integrate the same with Swift. And for that I have included the sdk and added the dependencies as well but when I am trying to call the methods of SDWebImage using . operatotor, it is not recognizing them. I have also created the Bridge file and included the

import <SDWebImage/UIImageView+WebCache.h>

import "UIImageView+WebCache.h"

in that. Here is my code and error I am getting!

Code:

cell.imageView.sd_setImageWithURL(sharedImageURL)

Error:

'UIImageView' does not have a member named 'sd_setImageWithURL'

Horse888 commented 9 years ago

Hi @harshcs , just test the demo(https://github.com/ZuYuanZhou/testSD.git), it's works well, you may miss something.

harshcs commented 9 years ago

@ZuYuanZhou Thanks a lot for the sample code! I have already followed the same steps but it didn't worked for me! Since a month I was banging my head and by just deleting and adding the bridge file, everything started working! It is very frustrating but many thanks to all of you for your inputs!

Horse888 commented 9 years ago

So how can I help you?

On 2014年12月22日, at 19:32, Harsh Mehrotra notifications@github.com wrote:

@ZuYuanZhou https://github.com/ZuYuanZhou Thanks a lot for the sample code! I have already followed the same steps but it didn't worked for me! Since a month I was banging my head and by just deleting and adding the bridge file, everything started working! It is very frustrating but many thanks to all of you for your inputs!

— Reply to this email directly or view it on GitHub https://github.com/rs/SDWebImage/issues/833#issuecomment-67827738.

umosys commented 9 years ago

I tried the optional binding technique suggested by @lubert , but still get the EXC_BAD_ACCESS error

self.manager.downloadImageWithURL(url,
            options: nil,
            progress: nil,
            completed: { [weak self] (image: UIImage!, error: NSError!, cacheType: SDImageCacheType!, finished: Bool!, imageURL: NSURL!) in
                if let wSelf = self {
                    // self is TableViewController
                    // previewView is Content View of a Table View Cell
                    wSelf.previewView.backgroundColor = UIColor(patternImage: image)
                }
        })

May I know if a solution is coming along, or any other ideas? Thanks :)

kmcgill88 commented 9 years ago

I'm seeing the same crash. Has anyone found a solution for this? I'm using Swift and Xcode 6.1.1.

alexdd55 commented 9 years ago

this works fine for me

SDManager.downloadImageWithURL(url, options: nil, progress: {
                receivedSize, expectedSize in
                    if receivedSize != expectedSize {

                    }
                }, completed: {
                    image, error, a , c ,s in
                    if image != nil {
                        cell.imageView?.image = image
                    }
            })
oanaandreea commented 9 years ago

I also had the same issue mentioned by forlooper. My code still crashed even if I used weak self. The solution I came up with was to declare a global variable having the same type as my controller and assigning the "self" object to this variable. The global variable can be accessed from the completion block. I am new to mobile development so I'm not sure how global variables impact the memory, but this works for me for now. Sample code:

var globalSetController: SettingsViewController!
class SettingsViewController: UIViewController {
     var blurView: FXBlurView!
      override func viewDidLoad() {
            globalSetController = self
            backgroundImgView.sd_setImageWithURL(urlImage, completed: { (image: UIImage!, error: NSError!, cacheType: SDImageCacheType!, imageURL: NSURL!) -> Void in
                globalSetController.blurView.updateAsynchronously(true, completion: nil)
                globalSetController = nil
            })
   ...
}
renatosc commented 9 years ago

Same problem here with Swift. Looking at ZuYuanZhou/testSD repo (Thanks for the example) as mentioned, I noticed that the function call needs to be:

sd_setImageWithURL()

and not

setImageWithURL()

as described in the README. Please update it, if possible.

Voidozzer commented 9 years ago

What worked for me was creating SDHelper in Obj-C, implementing sd_setimagewithurl there and call helper class method from my Swift viewcontroller

codepushr commented 9 years ago

Same problem here with EXC_BAD_ACCESS. Exception breakpoint halts on line 202 at dispatch_barrier_sync(self.barrierQueue, ^{ in SDWebImageDownloader.m

EDIT: My problem was that I allocated the SDWebImageDownloader on my own instead of using sharedDownloader. Now it's working.

mythodeia commented 9 years ago

based on comments above i am closing this