这本书一定是指过时的文档,因为你是对的,它没有提到任何关于保留计数的内容。它实际上保留了该对象。您需要考虑的方式不是保留计数(这是无用的),而是所有权。尤其是在使用 ARC 时。
当您将对象添加到 NSMutableArray 时,它会获得该对象的所有权(在 ARC 术语中,它具有对它的强引用)。
“ARC 带来了什么?”
ARC 没有什么不同。 ARC 所做的所有事情(除了一些优化)都是添加相同的 release、retain 和 autorelease 语句,这些语句与您在不使用 ARC 的情况下自己添加的语句相同。您只需要关心的是,一旦您将一个对象添加到数组中,它的寿命将至少与数组一样长。
arrayByAddingObject: 方法会创建一个新的NSArray(或NSMutableArray),其中包含您要传递的对象,并保持对传递对象的强引用。它创建的实际数组对象还没有引用,除非您将它分配给 ivar、属性或局部变量。你分配给它的东西决定了它的寿命。
基本上,即使没有 ARC,最好从所有权的角度来考虑对象生命周期,ARC 只是将其形式化。因此,因此,在使用框架时,无论何时发生或不发生保留都无关紧要,您只需对您的对象负责,直到您将所有权传递给另一个对象,并且您可以相信框架将使对象保持活动状态只要它需要。
当然,现在您必须凭直觉知道什么是所有权。例如,委托属性通常是 assign,或者在 ARC 中 unsafe_unretained 或 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,其中 self.someOtherArray = [someArray arrayByAddingObject:someObject]; 将实现相同的效果,但会使用属性访问器 (setSomeOtherArray:),或者您仍然可以使用 someOtherArray = [someArray arrayByAddingObject:someObject]; 直接设置 iVar (假设你@synthesized它)。
或者假设非 ARC,您可能已经声明了类似 @property (nonatomic, retain) NSArray *someOtherArray 的属性,其中 self.someOtherArray = [someArray arrayByAddingObject:someObject]; 的行为与 ARC 完全相同,但是当直接设置 iVar 时,您仍然需要手动添加该保留。
我希望这能澄清一点,如果有什么我掩盖或遗漏的地方,请告诉我。
正如您在评论中提到的,这里的关键是直观地知道一个对象何时会被视为由另一个对象拥有。幸运的是,Cocoa 框架遵循一套非常严格的约定,让您可以做出安全的假设:
- 在设置框架对象的
NSString 属性(例如UILabel 的text 属性)时,它总是被复制(如果有人知道反例,请评论或编辑)。因此,一旦通过它,您就不必担心您的字符串。复制字符串以防止可变字符串在传递后被更改。
- 设置
delegate 以外的任何其他属性时,它(几乎?)始终保留(或在 ARC 中为强引用)
- 在设置委托属性时,它(几乎?)总是一个分配(或弱引用)来防止循环保留循环。 (例如,对象
a 有一个属性b 是强引用的,b 有一个强引用的委托属性。您将a 设置为b 的委托。现在a 和b都相互强引用,并且两个对象都不会达到 0 的保留计数,也永远不会达到它的 dealloc 方法来释放另一个对象。NSURLConnection 是一个强烈引用它的委托的反例,因为它的委托是通过一种方法设置——参见下面的约定——并且约定在完成后消除或释放NSURLConnection,而不是在dealloc中,这将删除循环保留)
- 添加到数组或字典时,始终保留(或强引用)。
- 在调用方法和传递块时,它们总是被复制以将它们从堆栈(最初出于性能目的而创建的地方)移动到堆中。
- 接受对象参数并且不立即返回结果的方法是(总是?我想不出任何不)复制或保留(强引用)您传递的参数以确保方法可以对它们做它需要做的事情。例如,
NSURLConnection 甚至保留它的委托,因为它是通过方法传入的,而在设置委托时其他对象的 property 将不会保留,因为这是约定。
建议您在自己的课程中也遵循这些相同的约定,以保持一致性。
此外,不要忘记所有类的标题都可供您使用,因此您可以轻松查看属性是保留还是分配(或强或弱)。您无法检查方法对其参数的作用,但由于参数归接收者所有的约定,因此没有必要。