如果你有一个IBOutlet,但没有属性,它会被保留还是不会?

11

我觉得这个问题的文档不够清晰:

假设你正在使用 iOS(不是 Mac 的情况,无需提及差异)。 假设它严格为 4.0+(不需要提及旧操作系统中的差异)。 假设我们严格自动加载 NIB。

假设您有一个 UIViewController,名为 BigView。 假设在 NIB 文件中有十几个所谓的“顶级”项……可以是自定义控件、图像或其他任何内容。

假设您肯定会显式创建然后在应用程序运行期间多次摆脱 BigView。 所以:

对于 NIB 中的其中一个顶级项,有三种可能性

(1) 您根本没有它的任何 IBOutlet。

(2) 您已连接 IBOutlet - 但不是属性。

(3) 您已连接 IBOutlet 属性(为避免混淆,我们将说是保留属性)。

那么当释放 BigView 时该项会发生什么?

在情况(3)下,显然您必须明确释放。 如果不这样做,它将在视图消失后继续存在。 没问题。

在情况(1)下,我认为(但有人能够实际确认吗?)该项将在 BigView 消失时释放。

在情况(2)下,不清楚会发生什么……...

查看众所周知的参考链接:https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/LoadingResources/CocoaNibs/CocoaNibs.html 非常可疑:

"在 iOS 中,nib-loading 代码使用 setValue:forKey: 方法重新连接每个 outlet。当该方法找不到适当的访问器方法时,它会尝试其他方法。如果失败了 [那么会发生什么?告诉我们吧,苹果...] ..."

并向下滚动至“Nib Object Retention”:

"nib 文件中的对象的保留计数为 1,然后被自动释放 "。太棒了。

但是等等!再往下看几个单词...

然而,...如果有可用的 setter 方法,则使用它,或者如果没有可用的 setter 方法,则默认保留对象

他们在说什么?

他们是否意味着如果没有 setter 方法(ivar,但没有属性),它会再次被保留(除了在之前提到的“retain”之外)——还是他们只是重复自己,即“默认保留对象”的“retain”与他们刚刚立即谈论的“创建保留计数为 1 然后被自动释放”的“retain”相同。

如果这不是发生的事情,他们为什么要提及 autorelease?

确实,如果有人确切地知道这个问题的答案......你是怎么知道的? 通过问 DTS,还是通过测试,或者其他方法?我建议,关键的文档(刚刚粘贴)非常不清楚。

同样-如果您有一个 IBOutlet,但没有属性,连接到一个“顶级”对象...您负责释放它吗?它是否被保留?

换句话说...仅在情况(1)中,当 BigView 消失时,是否绝对会释放该物品呢?我当然认为是这种情况,但是谁知道呢?

问题是如果您使用 IBOutlet iVar,但没有属性...会发生什么...

我以前很愚蠢地从未考虑过这个/过于假设,有人有决定性的答案吗? 感谢!!


为记录,我已经制作了一个测试项目。

事实上(令我惊讶的是)将IB元素连接到IBOutlet中实际上似乎会添加一个保留

(我只能从糟糕的文档中推断,在那种情况下,您具体得到:Retain,Autorelease,Retain-导致平衡时保留一个。)

所以,这就是答案。

我将发布演示项目。 我还引导任何读者查看Jonah在下面的答案,对setValue:forKey:的行为进行无缝解释。 感谢


非常感谢。我也有完全相同的问题!在阅读苹果文档后,我感到非常困惑,不知道该相信什么。同时也感谢Jonah对此进行了澄清。 - Buju
这是个奇怪的问题,对吧 - 我同意你的看法。感谢你的点赞,因为它在这个网站上给了我“超级能力”之类的东西!我是世界之王!!! - Fattie
3个回答

11

我不明白为什么会有那么多混乱,我认为"Nib对象保留"文档已经很清楚地解释了发生了什么。让我们拆分一下并详细说明:

nib文件中的对象被创建时其保留计数是1,然后被自动释放。

ClassLoadedFromNib *loadedObject = [[[ClassLoadedFromNib alloc] initWithCoder:coder] autorelease];
然而,在重建对象层次结构时,UIKit使用setValue:forKey:方法重新建立对象之间的连接。
[filesOwner setValue:loadedObject forKey:nameOfIBOutlet];

如果没有可用的setter方法,则默认使用-setValue:forKey:或默认保留对象。

iOS中-setValue:forKey:的默认行为大致如下:

//lazy pseudocode
if ([self respondsToSelector:@selector(@"setKeyName:")]) {
  [self setKeyName:value];
}
else {
  object_setIvar(self, _keyName, [value retain]);
}

查看键-值编程指南以获取更详细的信息。除非文件的拥有者对象覆盖了-setValue:forKey:(或+accessInstanceVariablesDirectly-setValue:forUndefinedKey:),否则预计对象所有权将如上所述进行管理。


如果为nib文件中的对象定义了outlets,则应始终定义一个setter方法(或已声明的属性)来访问该outlet。Outlets的setter方法应保留其值,包含顶级对象的Outlets的setter方法必须保留其值以防止它们被释放。

允许nib加载直接设置ivar为外部保留的对象是令人困惑的。不要这样做。为您的outlets提供setter方法,以便加载的对象的所有权清晰明确。


如果未将顶级对象存储在outlets中,则必须保留由loadNibNamed:owner:options:方法返回的数组或数组中的对象之一,以防止这些对象过早释放。

未连接到outlet的对象已被自动释放。如果您以后要尝试访问它们,请保留它们或返回自-loadNibNamed:owner:options:的数组。


嗨Joneh - 简而言之,这是一个关于setValue:forKey:行为的精彩解释,谢谢你。(正如我详细描述的那样,文档存在严重缺陷...没有你我们就会很被动。)干杯! - Fattie

2

这是一个有趣的问题,但由于文档含义不明确,我认为最好的做法(也是苹果推荐的做法)是将所有outlets保留属性。在这种情况下,您知道发生了什么,没有必要做其他任何事情。


我没有dv,但是你只需要将某些东西变成outlet,如果IB需要看到它,只需要将其变成属性,如果你想要getter/setter。 - ingh.am
如果您需要一个插座,那是因为您计划保留从nib文件创建的某些内容的引用。使用属性来管理该引用可以避免上述问题,并使内存管理更加容易。当然,您可以直接使用ivar,但这也适用于任何属性。也许您的意思是只有在打算让除self之外的对象更改相关值时才使用属性,但这正是从nib加载对象时发生的情况。 - Caleb
@Joe Blow,我认为这是一个有趣的问题,但由于像这样的话题往往会让初学者感到困惑,所以我想提供最好的实用解决方案。我知道我发布的内容不是你想要的答案,如果有人因此而对这个答案投反对票,那也没关系。 - Caleb
你真的可以通过使用属性来提高性能吗?我认为它只是为该变量提供了一个getter和setter...除非必须使用,否则我从不使用属性,并且我从未注意到任何性能上的差异(尽管我可能是错的)。编辑:哦,等等,你是指不使用属性以获得稍微更快的性能吗?如果是这样,我理解!!我也这样做! - ingh.am
@Joe 是的,我们在这方面是一致的... 从我的经验来看,我只在需要时使用属性,因为a)我总是被教导在需要时创建getter和setter,b)我不能为每个变量都这样做! - ingh.am

0

情况1)如果对象没有被任何东西保留,它将在下一个自动释放池排空时被释放。

情况2)在你上面提到的答案中,Jon Hess已经描述了(并引用了文档),Mac OS X和iOS在这种情况下的区别。

Jon Hess正确还是Freeman正确?

在iOS的情况下,Hess和Freeman都说对象将被保留。他们之间没有矛盾。

仍然强烈建议为所有输出口设置器方法:

资源编程指南,Nib文件

如果您为nib文件对象定义了输出口,则应始终定义一个setter方法(或已声明的属性)来访问该输出口。输出口的setter方法应保留其值...


是的,它被保留了下来,当你完成它时应该释放它(通常在dealloc中)。实际上,我过去使用过这种方法,但现在我使用属性来代替,因为对于未来维护我的代码的人来说更不容易混淆。 - hoha
嗨Hoha。感谢你的指导。事实上,我做了一个测试项目证明了这种情况。我将发布它。我非常惊讶连接IB元素到IBOutlet的简单操作实际上增加了一个retain。再次感谢和祝福!抱歉有些白痴投票反对了你的评论! - Fattie

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