IBOutlet的只读属性是否有效,使用它是否更好?

6
在我接手的代码中,我看到了以下内容:
@property (readonly) IBOutlet UIImageView * bgImage;

当我期望一个保留内存模型时:

@property (readonly, retain) IBOutlet UIImageView * bgImage;

我不理解为什么第一个属性定义可以正常工作而不会出现问题。

另外,在dealloc中有一个release,就像这样:

-(void)dealloc
{
   [_bgImage release];
   [super dealloc];
}

我希望有人能解释以下这个问题。我跟原本的开发者交流过,他试图编写更简洁的代码,所以在内存模型中省略了retain(看起来是没有必要的)。
我想知道是否IBOutlet语句基本上像一个 ivar,在此只读(没有setter可用),因此默认的分配内存模型没有任何区别。
如果不希望IBOutlet有任何变化,那么使用没有内存模型的只读属性是否实际上是定义属性的首选方式?
2个回答

7
iOS上的nib加载器会创建nib中的对象并将其自动释放。当它建立与outlet的连接时,它会使用setValue:forKey:方法,该方法将调用该键的setter方法。如果没有定义setter,例如当IBOutlet是一个readonly属性时,在赋值之前对象仍然被保留。实际上,无论outlet是声明为retain还是assign,另一端的对象都由具有outlet的对象拥有。要么它由setter方法保留,要么在找不到setter时由setValue:forKey:保留。由于在第二种情况下没有其他可能的所有者,因此可以认为具有outlet的对象是所有者。因此,nib中的对象应在dealloc中释放。(这是在iOS中管理Nib对象资源编程指南的意译。)
我同意您的观点,应该通过更改属性属性来包含retain,以明确此内存条件。无论它是否为readonly似乎都没有影响(但请参见下文)。从概念上讲,对象是只读的,因此是否将其显式标记为只读取决于您是否认为这是一个IBOutlet的适当文档记录。
更新: Paul.s的下面评论促使我进行了快速测试。我创建了一个UIView子类,记录了其allocretainreleaseautorelease调用,将其实例粘贴到一个nib中,并通过属性向应用程序委托提供了一个IBOutlet
手动计算引用计数活动时,当属性为(readwrite, assign)时,实例的净计数为0。当属性以推荐方式声明为(readwrite, retain)时,它的净计数为+1,并且当它是(readonly, assign)时也是如此。所有这些都基本符合预期--当它是(readwrite, assign)时,使用分配setter来建立连接,并且不进行保留。当它是readonly时,连接机制会回退到执行自己的保留。
最有趣的是,当我尝试通过更改属性声明为(readwrite, assign)的视图的背景颜色(即,当它可能已被释放)来崩溃应用程序时,我看到了最后一次调用retain
我认为这归结于:遵循苹果的建议-他们知道幕后发生的事情,并且(除非有错误)不会引导您走错路。
另一个需要注意的事情是,像往常一样,担心绝对引用计数并没有太大用处——在两打调用retainrelease的过程中,计数曾一度上升到6——你只需要关注直接引起的保留和释放。

*当然,在ARC下会有所改变。我转述的信息在其章节的“Legacy Patterns”部分中。在ARC下,建议使用weak来定义IBOutlets,除非它们是顶层,这种情况下应该使用strong。这样做意味着您依赖于视图层次结构(视图保留其子视图)来维护自身。


无论outlet是声明为retain还是assign,另一端的对象都由具有该outlet的对象拥有(这不太清楚),只有当它设置为readonly时才是这样吗?如果它是(assign, readwrite),那么肯定会使用非保留setter,并且不会采取任何保留操作。我提到这一点,因为这是你回答中唯一有点模糊的部分。 - Paul.s
我认为是这样的...这似乎就是它所说的。 - jscs
2
@Paul.s:看到我的更新了吗——我决定手动计算参考活动,结果很有启示性。 - jscs
不错的好奇探究精神 - Paul.s
+1 标记为答案。但是,你会推荐哪种方式呢?如果你不打算更改 IBOutlet 实例,那么只读的方式是否更好?个人喜欢 (readwrite, retain)。通常我会这样写 @property (nonatomic, retain) IBOutlet UIView *myView(readwrite 是默认值,所以不需要显式地写出来)。 - Sam
1
@Sam:我认为你应该采用苹果的建议:(readwrite, nonatomic, retain)(正如你所说,可以省略显式的readwrite)。并不是要不尊重你的同事,简洁当然有助于可读性,但有时编写符合大家期望的代码更为重要。如果他按照推荐的方式做了,你就不必花费时间来研究其含义了。虽然这是一件有趣的事情,但你可能本可以在那段时间内完成(更重要的)事情。 - jscs

2
我向苹果报告了一个问题,如果你创建IBOutlet实例变量而不是属性,那么Xcode仍会自动在dealloc等方法中创建release。对于iOS应用程序的Xcode似乎总是为IBOutlet创建释放,无论它是否正确。
个人认为我不喜欢将IBOutlet作为属性,因为这意味着你将它们声明为可读写的,这意味着它们被记录为可读写的,但大多数情况下(几乎总是),IBOutlet应该概念上是只读的。显然,它们必须是可读写的以进行初始设置。

你可以在公共接口中声明 @property (nonatomic, readonly),而在类扩展中声明 @property (nonatomic) IBOutlet - Rudolf Adamkovič

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接