使用ARC的Objective-C:自定义setter不会保留

时间:2011-08-14 10:18:55

标签: objective-c automatic-ref-counting

  

注意:这是由于一些XCode测试版中的错误已经修复。如果您遇到ARC问题,此问题和答案可能无法帮助您。


我正在将我的项目从手动引用计数迁移到ARC,并且偶然发现了一个问题:如何确保保留属性的自定义setter实际保留?

myClass.h中,我声明了一个属性:@property (retain) NSDate *date。无论我是手动设置__strong ivar还是自动生成它都无关紧要。

在实施中,我当然有@synthesize date,并实施了自定义设置器(或只是download the demo Xcode project):

- (void)setDate:(NSDate *)newDate
{
  if (allowedToSetNewDate)
  {
    date = newDate;
  }
}

这似乎没有保留日期,并且当newName被(自动)释放时,当我尝试访问message sent to deallocated instance时(如果已启用Zombie,则会自动释放)给我myClass.date;否则,它只是默默地崩溃。)

更改setter以使用date = [newDate copy]可以解决错误,但实际上并不是我想要的。删除自定义setter也有效,但显然不是必需的。

我在这里缺少什么?如何确保保留属性的自定义setter实际保留在ARC环境中?这似乎是一项基本而且常见的任务,我认为我忽略了一些非常明显的事情。

(注意:这不属于任何Apple NDA的条款,因为ARC作为LLVM的一部分公开发布)

编辑:我已经创建了一个小型Xcode项目来演示该问题并将其上传到github。随意download it并玩游戏。我的机智已经结束了(尽管我的机智今天并不是最好的,但不可否认)。

编辑:对于此示例项目,此问题已解决(请参阅已接受的答案)。不幸的是,在我无法分享的更大的项目中,问题仍然存在。作为一种解决方法,我添加了复合setter的重复strong属性(ivars不起作用)。新的自定义setter现在看起来像这样:

- (void)setDate:(NSDate *)newDate
{
  if (allowedToSetNewDate)
  {
    self.date_arcretain = newDate; //this property is only there as a workaround. ARC properly retains it, but only if the setter is synthesized
    date = newDate;
  }
}

2 个答案:

答案 0 :(得分:12)

这对我来说似乎是个错误;你的代码应该没问题。如果您还没有这样做,请在http:// bugreport.apple.com上提交一个错误,并附上您的示例项目。

编辑:在进一步检查您的示例项目时,这不是错误。

示例项目中过度发布的对象 NSDate实例。您可以完全注释掉示例项目中的tc.date = now调用,但您仍会看到相同的崩溃。事实上,你可以完全取出NSDate的东西。过度释放的对象实际上是TestVC对象本身。

这是正在发生的事情。

在iOS 4.0中,UIWindow获得了rootViewController属性。之前您只需在启动应用程序时调用[self.window addSubview:myRootcontroller.view],此更改现在意味着窗口实际上会引用根视图控制器。这对于传递旋转通知等非常重要。过去,我相信UIWindow会在添加第一个子视图时自动尝试设置rootViewController(如果尚未设置),但是在您的示例项目中显然没有发生。这可能是由于您创建视图的方式,或者可能是由于iOS 5.0中的更改。无论哪种方式,都没有记录行为,所以你不能依赖它发生。

在大多数情况下,您的app委托将有一个指向根视图控制器的ivar。这不是严格要求的,但通常会发生什么。但是,在您提供的示例项目中,视图控制器不属于应用程序委托。你也没把它设置为窗口的根视图控制器。因此,在-application:didFinishLaunchingWithOptions:方法结束时, nothing 会留下对视图控制器的强引用。您的应用代表没有抓住它,窗口本身并没有坚持下去。因此,ARC将其视为局部变量(它是),并在方法结束时释放它。

当然,这并不会改变您的UIButton仍有针对您的控制器的操作方法的事实。但正如文档中所述,-addTarget:action:forControlEvents:不会保留目标。所以UIButton有一个对你的视图控制器的悬空引用,现在已经解除分配,因为没有人强烈引用它。因此崩溃。

解决此问题的方法是在应用代理中更改此行:

[self.window addSubview:tc.view];

到此:

self.window.rootViewController = tc;

通过这一次改变,现在一切正常。

编辑:还要确保未启用“预先检查ARC迁移代码”设置,因为这会导致编译器将代码视为手动管理,并且这不会插入正确的保留/释放电话。

答案 1 :(得分:2)

我用你的项目将它固定在我的Mac上。错误发生在AppDelegate中,而不在您的日期属性中。使视图控制器成为AppDelegate中的可保留属性。目前,您的视图控制器已自动释放,包括其所有属性。