11

注意:这是由于一些 XCode beta 版本中的一个 bug 导致的,这个 bug 早已被修复。如果您对 ARC 有疑问,此问答可能对您没有帮助。


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

myClass.h中,我声明了一个属性:@property (retain) NSDate *date。我是手动设置__strongivar 还是让它自动生成都没有关系。

在实现中,我当然有@synthesize date,并实现了一个自定义设置器(或者只是下载演示 Xcode 项目):

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

这似乎没有保留日期,并message sent to deallocated instance在 newName (自动)发布它来自的地方,myClass.date稍后尝试访问时给我(假设 Zombie 已启用;否则,它只是默默地崩溃)。

更改要使用的设置器可以date = [newDate copy]解决该错误,但这并不是我真正想要的。删除自定义设置器也可以,但显然是不可取的。

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

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

编辑:我创建了一个小型 Xcode 项目来演示该问题并将其上传到 github。随意下载并玩。我已经束手无策了(尽管我的机智今天不是最好的,诚然)。

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

- (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;
  }
}
4

2 回答 2

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 中的更改。无论哪种方式,它都没有记录在案的行为,所以你不能依赖它的发生。

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

当然,这并没有改变您UIButton仍然有一个针对您的控制器的操作方法的事实。但如文档中所述,-addTarget:action:forControlEvents:不保留目标。因此UIButton,您的视图控制器有一个悬空引用,由于没有人对它有强引用,它现在已被释放。因此崩溃。

解决此问题的方法是在您的应用程序委托中更改此行:

[self.window addSubview:tc.view];

对此:

self.window.rootViewController = tc;

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

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

于 2011-08-14T15:32:18.257 回答
2

I fixed it on my Mac with your project. The error is in your AppDelegate, not in your date property. Make the view controller a retainable property in AppDelegate. Currently your view controller is autoreleased, including all it's properties.

于 2011-08-14T16:02:31.920 回答