简体   繁体   English

使用委托模式时避免EXC_BAD_ACCESS

[英]Avoiding EXC_BAD_ACCESS when using the delegate pattern

A have a view controller, and it creates a "downloader" object, which has a reference to the view controller (as a delegate). A有一个视图控制器,它创建一个“下载器”对象,它有一个对视图控制器的引用(作为委托)。 The downloader calls back the view controller if it successfully downloads the item. 如果成功下载项目,下载程序将回调视图控制器。 This works fine as long as you stay on the view, but if you navigate away before the download is complete I get EXC_BAD_ACCESS . 只要您保持在视图上,这样就可以正常工作,但如果您在下载完成之前导航,我会获得EXC_BAD_ACCESS I understand why this is happening, but is there any way to check if an object is still allocated? 我理解为什么会发生这种情况,但有没有办法检查对象是否仍然被分配? I tried to test using delegate != nil , and [delegate respondsToSelector:] , but it chokes. 我尝试使用delegate != nil[delegate respondsToSelector:] ,但它会窒息。

if (!self.delegate || ![self.delegate respondsToSelector:@selector(downloadComplete:)]) {
  // delegate is gone, go away quietly
        [self autorelease];
        return;
    }
else {
  // delegate is still around
  [self.delegate downloadComplete:result];
}

I know I could, 我知道我可以,

a) have the downloader objects retain the view controller a)让下载器对象保留视图控制器

b) keep an array of downloaders in the view controller, and set their delegate values to nil when I deallocate the view controller. b)在视图控制器中保留一组下载程序,并在取消分配视图控制器时将其委托值设置为nil。

But I wonder if there is an easier way, where I just test if the delegate address contains a valid object? 但我想知道是否有一种更简单的方法,我只测试委托地址是否包含有效对象?

I just ran into this problem and solved it. 我刚遇到这个问题并解决了它。 For ARC, the solution is to use the weak attribute instead of assign . 对于ARC,解决方案是使用weak属性而不是assign

The crash come because the delegate 崩溃是因为代表

  1. Has an assign attribute, AND 有一个assign属性,AND
  2. Has been deallocated. 已被解除分配。

The solution is to use the weak attribute, because when the object deallocates, the pointer WILL be set the nil . 解决方案是使用weak属性,因为当对象解除分配时,指针设置为nil So when your code calls respondsToSelector on a nil , Objective C will ignore the call, and not crash. 因此,当您的代码在nil上调用respondsToSelector时,Objective C将忽略该调用,而不是崩溃。

In your code, when you attempt to call the respondsToSelector method on delegate , you get a EXC_BAD_ACCESS. 在您的代码中,当您尝试在delegate上调用respondsToSelector方法时,您将获得EXC_BAD_ACCESS。 This is because objects that use the assign property will not be set to nil when they are deallocated. 这是因为在取消分配时,使用assign属性的对象不会设置为nil (Hence why doing a !self.delegate before the respondsToSelector does not prevent the responseToSelector from being called on a deallocated object, and still crashes your code) (因此,为什么在respondsToSelector之前执行!self.delegate不会阻止在释放的对象上调用responseToSelector ,并且仍然会崩溃您的代码)

As already mentioned, using a strong or assign attribute on a delegate (as many have mentioned) in ARC will result in a retain cycle. 如前所述,在ARC中使用委托上的strongassign属性(正如许多人所提到的)将导致保留周期。 So don't do it, you don't need to. 所以不要这样做,你不需要。

No, you can't (usefully) "test if an address contains a valid object". 不,你不能(有用)“测试地址是否包含有效对象”。 Even if you were able to grub around inside the internals of the memory allocation system and determine that your address points to a valid object, that would not necessarily mean that it was the same object that you were previously referring to: the object could have been deallocated and another object created at the same memory address. 即使您能够在内存分配系统的内部进行内容并确定您的地址指向有效对象,这也不一定意味着它您之前引用的对象相同 :该对象可能已经存在取消分配和在同一内存地址创建的另一个对象。

Retaining the delegate is the usual way to solve this. 保留代表是解决这个问题的常用方法。 Your option (b) breaks object encapsulation, and might have thread-safety issues. 您的选项(b)打破了对象封装,并可能存在线程安全问题。

I also have problems with delegate weak reference, and currently I have only one solution for this problem: use strong reference for delegate, and manually set self.delegate = nil; 我也有委托弱引用的问题,目前我只有一个解决这个问题的方法:使用强引用委托,并手动设置self.delegate = nil; after my code is complete. 我的代码完成后。 This solution works for me in async images loading, where you have some life cycle with visible ending. 这个解决方案适用于异步图像加载,你有一些生命周期可见的结束。

I would just write 我会写的

SEL slc = @selector(theSlc);
if ([delegate respondsToSelector:slc]) {
    [delegate performSelector:slc];
}

If the object is valid the method will be called, otherwise not. 如果对象有效,则调用该方法,否则不调用。 You do not have to check for 你不必检查

self.delegate != nil

I came across this question because my "downloader" object was giving me EXC_BAD_ACCESS. 我遇到了这个问题,因为我的“下载器”对象给了我EXC_BAD_ACCESS。 My solution was the cancel the downloader object right before I released it. 我的解决方案是在我发布它之前取消下载器对象。 Assuming your using NSURLConnection in your downloader object, call the cancel method on it. 假设您在下载程序对象中使用NSURLConnection,请在其上调用cancel方法。

Its important to note that if NSURLConnection is not currently downloading anything then calling cancel will result in a crash. 重要的是要注意,如果NSURLConnection当前没有下载任何东西,那么调用cancel将导致崩溃。 You will need some logic to check if a download is in progress. 您需要一些逻辑来检查下载是否正在进行中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM