使用委托模式时避免EXC_BAD_ACCESS

时间:2010-04-21 03:44:30

标签: iphone objective-c memory-management exc-bad-access

A有一个视图控制器,它创建一个“下载器”对象,它有一个对视图控制器的引用(作为委托)。如果成功下载项目,下载程序将回调视图控制器。只要您保持在视图中,这样就可以正常工作,但如果您在下载完成之前离开,我会得到EXC_BAD_ACCESS。我理解为什么会发生这种情况,但有没有办法检查对象是否仍然被分配? 我尝试使用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];
}

我知道我可以,

a)让下载程序对象保留视图控制器

b)在视图控制器中保留一组下载程序,并在取消分配视图控制器时将其委托值设置为nil。

但是我想知道是否有更简单的方法,我只测试代理地址是否包含有效对象?

5 个答案:

答案 0 :(得分:27)

我刚遇到这个问题并解决了它。对于ARC,解决方案是使用weak属性而不是assign

崩溃是因为代表

  1. 具有assign属性,AND
  2. 已被解除分配。
  3. 解决方案是使用weak属性,因为当对象解除分配时,指针设置为nil。因此,当您的代码在respondsToSelector上调用nil时,Objective C将忽略该调用,而不会崩溃。

    在您的代码中,当您尝试在respondsToSelector上调用delegate方法时,您会获得EXC_BAD_ACCESS。这是因为使用assign属性的对象在取消分配时不会设置为nil。 (因此,为什么在!self.delegate之前执行respondsToSelector不会阻止在解除分配的对象上调用responseToSelector,并且仍然会使代码崩溃)

    如前所述,在ARC中使用委托上的strongassign属性(正如许多人所提到的)将导致保留周期。所以不要这样做,你不需要。

答案 1 :(得分:10)

不,你不能(有用)“测试地址是否包含有效对象”。即使您能够在内存分配系统的内部进行内容并确定您的地址指向有效对象,这也不一定意味着它是您之前引用的相同的对象to:对象可能已被释放,另一个对象在同一个内存地址创建。

保留代表是解决此问题的常用方法。您的选项(b)打破了对象封装,可能存在线程安全问题。

答案 2 :(得分:1)

我会写

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

如果对象有效,则调用该方法,否则不调用。您无需检查

self.delegate != nil

答案 3 :(得分:1)

我遇到了这个问题,因为我的“下载程序”对象给了我EXC_BAD_ACCESS。我的解决方案是在我发布它之前取消下载器对象。假设您在下载程序对象中使用NSURLConnection,请在其上调用cancel方法。

重要的是要注意,如果NSURLConnection当前没有下载任何内容,那么调用cancel将导致崩溃。您需要一些逻辑来检查下载是否正在进行中。

答案 4 :(得分:1)

我也有委托弱引用的问题,目前我只有一个解决这个问题的方法:使用强引用委托,并手动设置self.delegate = nil;我的代码完成后。这个解决方案适用于异步图像加载,你有一些生命周期可见的结束。