这本书一定是指过时的文档,因为你是对的,它没有提到任何关于保留计数的内容。它实际上保留了该对象。您需要考虑的方式不是保留计数(这是无用的),而是所有权。尤其是在使用 ARC 时。
当您将对象添加到 时NSMutableArray
,它会获得该对象的所有权(在 ARC 术语中,它具有对它的强引用)。
“ARC为此带来了什么?”
ARC 没有什么不同。ARC 所做的所有事情(除了一些优化)都是添加相同的 release、retain 和 autorelease 语句,这些语句与您在不使用 ARC 的情况下自己添加的语句相同。您需要关心的是,一旦将对象添加到数组中,它将至少与数组一样长。
并且该arrayByAddingObject:
方法创建一个包含您正在传递的对象的新NSArray
(或NSMutableArray
),并保持对传递对象的强引用。它创建的实际数组对象还没有引用,除非您将它分配给 ivar、属性或局部变量。你分配给它的东西决定了它的寿命。
基本上即使没有 ARC,最好从所有权的角度考虑对象生命周期,ARC 只是将其形式化。因此,因此,在使用框架时,无论何时发生或不发生保留都无关紧要,您只需对您的对象负责,直到您将所有权传递给另一个对象并且您可以相信框架将使对象保持活动状态只要它需要它。
当然,现在你必须凭直觉知道什么是所有权。例如,委托属性通常assign
是 ARCunsafe_unretained
或ARC 中的weak
,以防止循环保留循环(两个对象彼此保留),尽管有时保留/强,因此您需要逐个研究这些。
并且在像键值观察和 NSNotification 观察你正在观察的对象这样的情况下也不会保留观察者。
但这些确实是规则的例外。一般来说,你可以假设一个强参考。
关于上面这句话:“它创建的实际数组对象还没有引用,除非您将它分配给 ivar、属性或局部变量。您分配给它的内容决定了它的生命周期。” 我将尝试解释:
当您运行这段代码时:[someArray arrayByAddingObject:someObject];
您已经实例化了一个新对象NSArray
或NSMutableArray
对象(取决于对象类型someArray
),但实际上并未将其分配给任何引用。这意味着如果您使用 ARC,它可能会在之后立即释放,或者如果不使用 ARC,它将在它的 autoreleasepool 耗尽时被释放(可能在该线程的 runloop 的下一次迭代中)。
现在,如果您这样做:NSArray *someOtherArray = [someArray arrayByAddingObject:someObject];
您现在有一个对新创建的数组的引用,称为 someOtherArray。在这种情况下,这是一个局部变量,其作用域仅位于它所在的集合中{
}
(因此它可能位于if
语句、循环或方法中。现在,如果您对它不做任何其他事情,它会在它结束后的某个时间死掉范围结束(不能保证它会立即死亡,但这并不重要,你不能假设它的寿命更长)。
现在,如果在您的类中,您在标头中声明了一个 iVar(实例变量),例如NSArray *someOtherArray;
(在 ARC 中默认情况下是强的)并且您someOtherArray = [someArray arrayByAddingObject:someObject];
在类中的某个位置运行,则该对象将一直存在,直到您删除引用(someOtherArray = nil
),您覆盖引用 ( someOtherArray = someThirdArray
),或者类被释放。如果您不使用 ARC,则必须确保保留它以达到相同的效果(someOtherArray = [[someArray arrayByAddingObject:someObject] retain];
这实际上是 ARC 在幕后所做的)。
或者你可能有一个声明的属性,而不是像@property (nonatomic, strong) NSArray *someOtherArray
inself.someOtherArray = [someArray arrayByAddingObject:someObject];
那样会达到相同的效果,但会使用属性访问器 ( setSomeOtherArray:
),或者你仍然可以使用someOtherArray = [someArray arrayByAddingObject:someObject];
直接设置 iVar(假设你@synthesized
这样做)。
或者假设非 ARC,您可能已经声明了类似于 ARC 的属性@property (nonatomic, retain) NSArray *someOtherArray
,self.someOtherArray = [someArray arrayByAddingObject:someObject];
但是当直接设置 iVar 时,您仍然需要手动添加该保留。
我希望能把事情弄清楚一点,如果有什么我掩盖或遗漏的地方,请告诉我。
正如您在评论中提到的,这里的关键是直观地知道一个对象何时会被视为由另一个对象拥有。幸运的是,Cocoa 框架遵循一组非常严格的约定,允许您做出安全的假设:
- 在设置
NSString
框架对象的属性(例如text
a 的属性UILabel
)时,它总是被复制(如果有人知道反例,请评论或编辑)。因此,一旦通过它,您就不必担心您的字符串。复制字符串以防止可变字符串在传递后被更改。
- 当设置除 之外的任何其他属性时
delegate
,它(几乎?)总是保留(或 ARC 中的强引用)
- 在设置委托属性时,它(几乎?)总是一个分配(或弱引用)以防止循环保留循环。(例如,对象
a
有一个b
强引用的属性和b
一个强引用的委托属性。您设置a
为b
. 现在a
和b
都相互强引用,并且两个对象都不会达到保留计数 0 并且永远不会达到它的 dealloc 方法来释放另一个对象。NSURLConnection
是一个强烈引用它的委托的反例,因为它的委托是通过一个方法设置的——参见下面的约定——并且它是NSURLConnection
在完成后取消或释放一个约定而不是 in dealloc
,这将删除循环保留)
- 添加到数组或字典时,它总是被保留(或强引用)。
- 当调用方法并传递块时,它们总是被复制以将它们从堆栈(最初出于性能目的创建它们的地方)移动到堆中。
- 接受对象参数并且不立即返回结果的方法是(总是?我想不出任何不)复制或保留(强引用)您传递的参数以确保该方法可以执行他们需要什么。例如,
NSURLConnection
甚至保留它的委托,因为它是通过方法传入的,而在设置其他对象的委托属性时不会保留,因为这是约定。
建议您在自己的类中也遵循这些相同的约定以保持一致性。
此外,不要忘记所有类的标题都可供您使用,因此您可以轻松查看属性是保留还是分配(或强或弱)。您无法检查方法对其参数的作用,但由于参数归接收者所有的约定,因此没有必要。