ARC和autorelease

autorelease用于返回的函数对象,所以调用者不拥有所有权,被调用者将在未来释放对象。

然而,ARC可以计算调用者的所有权并在使用后释放它,也就是说,它可以像C ++中的智能指针一样行为。 使用ARC,它可以摆脱autorelease,因为autorelease是非确定性的。

我问这个问题的原因是,我看到ARC中的返回对象调用dealloc比非ARC代码。 这导致我认为,ARC可以像智能指针一样的行为,可以使autorelease无用。 这是真的还是可能的? 我可以考虑autorelease有用的唯一的事情是multithreading或networking代码,因为当对象传递时可能不容易计算所有权。

谢谢你的想法。

这里是新的编辑,使事情清楚:

与autorelease

+ (MyClass*) myClass { return [[[MyCClass alloc] init] autorelease]; } - doSomething { MyClass *obj = [MyClass myClass]; } 

用ARC:

 + (MyClass*) myClass { return [[MyCClass alloc] init]; // no autorelease } - doSomething { MyClass *obj = [MyClass myClass]; // insert [obj release] } 

所以,我们真的不需要autorelease。

自动释放作为一种机制仍被ARC使用,而且ARC编译代码被devise为与MRC编译代码无缝地互操作,所以自动释放机器就在附近。

首先,不要以参考计数的方式来思考,而应该从所有权利益的angular度来考虑 – 只要对象中存在被宣告的所有权利益,那么对象就会生存,当没有所有权利益时,它就被销毁。 在MRC中,您通过使用retain或创build新对象来声明所有权权益; 你放弃所有权利益。

现在,当一个被调用方法创build一个对象并希望将其返回给调用者时,被调用者正在离开,因此它需要放弃所有权利益,因此调用者需要声明它的所有权利益或者对象可能被销毁。 但是有一个问题,被调用者在调用者接收对象之前就完成了 – 所以当调用者放弃其所有权利益时,在调用者有机会声明它的兴趣之前对象可能被销毁 – 不好。

两个解决scheme用于解决这个问题:

1)该方法声明所有权的利益从被调用者转移到调用者 – 这是用于initcopy等方法的模型。 被叫人永​​远不会通知其放弃所有权权益,而被叫人从不声明所有权利益 – 通过协议,被叫方只是接pipe所有权利益,以后再放弃。

2)该方法被声明为返回一个值,在该值中,调用者没有所有权利益,但是其他人将在一段短时间内保持所有权利益 – 通常直到当前运行循环周期结束。 如果调用者想要使用比这更长的返回值,则必须声明自己的所有者权益,否则可以依赖于拥有所有者权益的其他人,从而使对象留在附近。

问题是谁能维持所有者权益? 它不能成为被调用方法,因为它即将离开。 进入“autorelease池” – 这只是一个对象,任何人都可以转移所有权利益,所以对象将留在一段时间。 自动释放池在被指示的时候会以这种方式放弃所有传送给它的对象的所有权 – 通常在当前运行循环结束时。

现在,如果上述说明有意义(即,如果我已经清楚地说明了),则可以看到方法(2)并不是真正需要的,因为您始终可以使用方法(1)。 但是 ,这是一个至关重要的问题, 但是在MRC中,对于程序员来说还有很多工作要做 – 从方法中获得的每一个价值都带有一个必须在某个点上被pipe理和放弃的所有权利益 – 生成一个string来输出它? 那么你需要放弃你对这个临时弦的兴趣…所以(2)让生活变得容易很多。

另一方面,电脑只是快速白痴,代表聪明的程序员计算事物并插入代码来放弃所有权利益是他们非常适合的事情。 所以ARC 不需要自动释放池。 但是它可以让事情变得更容易,更高效,而后台ARC优化了它的使用 – 查看Xcode中的汇编程序输出,您将看到名为类似于“retainAutoreleasedReturnValue”的例程的调用…

所以你是对的, 不需要它,但它仍然是有用的 – 但是在ARC下,你可以(通常)忘记它,即使存在。

HTH比它可能混淆!

autorelease用于返回的函数对象,所以调用者不拥有所有权,被调用者将在未来释放对象。

如果autoreleased,它会被添加到autorelease池。 当自动释放池被耗尽时,将执行延期释放。 一个函数/方法不需要返回一个自动释放对象(例如,它可能是一个没有收到保留/自动释放周期的伊娃)。

然而,ARC能够计算调用者的所有权并在使用后释放它,也就是说,它可以像C ++中的智能指针一样行为。 使用ARC,它可以摆脱autorelease,因为autorelease是非确定性的。

它有潜力 。 没有保证。 这里最大的问题是编译器不知道/关心任意调用的返回对象的内存机制。 它不能假定一个对象是如何返回的,因为ARC是一个在MRC之前的新增加。 这很重要,因为它使ARC程序与使用手动保留/释放的程序兼容。 例如,Foundation.framework可以使用ARC,也可以使用MRC,也可以同时使用两者。 它也可以调用使用较旧的工具链构build的API。 所以这有利于保持大量的现有代码可用。

我问这个问题的原因是,我看到ARC中的返回对象调用dealloc比非ARC代码。

有一个可选的方法来返回一个对象 – 请参阅关于程序集的CRD的答案(+1)以及编译器插入的用于执行引用计数操作的调用,例如retainAutoreleasedReturnValue

无论如何,不​​能保证ARC的生命周期总是会减less的。 懂得执行程序的程序员可以最大限度地减less使用寿命,并可以重新计算操作,因为ARC具有更严格的生命周期和所有权要求。

这导致我认为,ARC可以像智能指针一样的行为,可以使autorelease无用。 这是真的还是可能的?

从理论上讲,我不明白为什么autorelease池不能用于新系统 。 然而,我认为有太多的依赖于autorelease池的现有代码来解除这个限制 – 我认为他们需要逐步采用新的可执行格式(ObjC垃圾收集的情况就是这样),并且审查大量现有的API,这样的重大过渡计划才能成功。 此外,一些API可能只需要被删除。 API可能需要加强所有权才能完成,但大部分API已经在已经被移植到ARC的程序中完成。 哎呀,即使编译器可以(扩展到)内部使用智能指针的forms来传递和返回objctypes和自动释放池可以在这样的系统中被淘汰。 再次,这将需要大量的代码被迁移。 所以这样的升级就像是一个ARC V2。

我可以考虑autorelease有用的唯一的事情是multithreading或networking代码,因为当对象传递时可能不容易计算所有权。

不是一个问题 – autorelease池是线程本地。 除了这种系统之外,我没有看到一个问题(除非你依赖于竞争条件,这显然是一个坏主意)。

ARC和autorelease在代码中的区别:

ARC:

 -somefunc { id obj = [NSArray array]; NSLog(@"%@", obj); // ARC now calls release for the first object id obj2 = [NSArray array]; NSLog(@"%@", obj2); // ARC now calls release for the second object } 

Autorelease:

 -somefunc { id obj = [NSArray array]; NSLog(@"%@", obj); id obj2 = [NSArray array]; NSLog(@"%@", obj2); } // Objects are released some time after this 

基本上,一旦一个variables不再被使用在一个范围内,ARC就会工作,而autorelease一直等到它到达主循环,然后调用池中所有对象的release 。 ARC在范围内使用,autorelease在function范围之外使用。

autorelease仍然在ARC下使用。 ARC只是给你打电话,聪明地把它短路。 这里是一个演示 ,究竟是如何工作的,我将在这里复制,以防博客文章消失; 所有应得的信贷马特加洛韦。

所以考虑下面的方法:

 void foo() { @autoreleasepool { NSNumber *number = [NSNumber numberWithInt:0]; NSLog(@"number = %p", number); } } 

当然这完全是人为的,但它应该让我们看看发生了什么事情。 在非ARC的土地上,我们假设这个数字将被分配在numberWithInt中:并且返回自动释放。 所以当autorelease池下一次耗尽时,它会被释放。 那么让我们来看看是不是发生了什么(像往常一样,这是ARMv7的说明):

  .globl _foo .align 2 .code 16 .thumb_func _foo _foo: push {r4, r7, lr} add r7, sp, #4 blx _objc_autoreleasePoolPush movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_-(LPC0_0+4)) movs r2, #0 movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_-(LPC0_0+4)) mov r4, r0 movw r0, :lower16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC0_1+4)) LPC0_0: add r1, pc movt r0, :upper16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC0_1+4)) LPC0_1: add r0, pc ldr r1, [r1] ldr r0, [r0] blx _objc_msgSend mov r1, r0 movw r0, :lower16:(L__unnamed_cfstring_-(LPC0_2+4)) movt r0, :upper16:(L__unnamed_cfstring_-(LPC0_2+4)) LPC0_2: add r0, pc blx _NSLog mov r0, r4 blx _objc_autoreleasePoolPop pop {r4, r7, pc} 

嗯,是。 这正是发生的事情。 我们可以看到呼叫推动一个autorelease池,然后呼叫numberWithInt:然后调用来popup一个自动释放池。 正是我们所期望的。 现在让我们来看看在ARC下编译的完全相同的代码:

  .globl _foo .align 2 .code 16 .thumb_func _foo _foo: push {r4, r5, r7, lr} add r7, sp, #8 blx _objc_autoreleasePoolPush movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_-(LPC0_0+4)) movs r2, #0 movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_-(LPC0_0+4)) mov r4, r0 movw r0, :lower16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC0_1+4)) LPC0_0: add r1, pc movt r0, :upper16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC0_1+4)) LPC0_1: add r0, pc ldr r1, [r1] ldr r0, [r0] blx _objc_msgSend @ InlineAsm Start mov r7, r7 @ marker for objc_retainAutoreleaseReturnValue @ InlineAsm End blx _objc_retainAutoreleasedReturnValue mov r5, r0 movw r0, :lower16:(L__unnamed_cfstring_-(LPC0_2+4)) movt r0, :upper16:(L__unnamed_cfstring_-(LPC0_2+4)) mov r1, r5 LPC0_2: add r0, pc blx _NSLog mov r0, r5 blx _objc_release mov r0, r4 blx _objc_autoreleasePoolPop pop {r4, r5, r7, pc} 

注意对objc_retainAutoreleasedReturnValue和objc_release的调用。 发生了什么事情,ARC已经确定,它并不需要担心autorelease池,因为它可以简单地告诉autorelease不会发生(调用objc_retainAutoreleasedReturnValue),然后释放对象本身。 这是可取的,因为这意味着自动释放逻辑不必发生。

请注意,自动释放池仍然需要被推送和popup,因为ARC无法知道在numberWithInt调用中发生了什么,而NSLog知道对象是否会被放入池中。 如果它知道他们没有自动发布任何东西,那么它实际上可以摆脱推动和stream行。 也许这种逻辑会在未来的版本中出现,尽pipe我不太确定这种语义如何工作。

现在让我们考虑另外一个例子,它是我们想要使用autorelease池块范围之外的数字。 这应该告诉我们为什么ARC是一个奇迹。 考虑下面的代码:

 void bar() { NSNumber *number; @autoreleasepool { number = [NSNumber numberWithInt:0]; NSLog(@"number = %p", number); } NSLog(@"number = %p", number); } 

你可能(正确)认为,即使看起来完全无害,这也会造成问题。 这是一个问题,因为号码将被分配在autorelease池块内,当autorelease池popup时将被释放,但是在释放之后被使用。 呃哦! 让我们看看如果我们没有启用ARC编译它是正确的:

  .globl _bar .align 2 .code 16 .thumb_func _bar _bar: push {r4, r5, r6, r7, lr} add r7, sp, #12 blx _objc_autoreleasePoolPush movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_-(LPC1_0+4)) movs r2, #0 movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_-(LPC1_0+4)) mov r4, r0 movw r0, :lower16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC1_1+4)) LPC1_0: add r1, pc movt r0, :upper16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC1_1+4)) LPC1_1: add r0, pc ldr r1, [r1] ldr r0, [r0] blx _objc_msgSend movw r6, :lower16:(L__unnamed_cfstring_-(LPC1_2+4)) movt r6, :upper16:(L__unnamed_cfstring_-(LPC1_2+4)) LPC1_2: add r6, pc mov r5, r0 mov r1, r5 mov r0, r6 blx _NSLog mov r0, r4 blx _objc_autoreleasePoolPop mov r0, r6 mov r1, r5 blx _NSLog pop {r4, r5, r6, r7, pc} 

很明显,我们没有要求保留,释放或自动释放,因为我们没有明确地表示,我们没有使用ARC。 我们可以在这里看到,它的编译完全和我们之前的推理一样。 那么让我们看看ARC给我们一个帮助时的样子:

  .globl _bar .align 2 .code 16 .thumb_func _bar _bar: push {r4, r5, r6, r7, lr} add r7, sp, #12 blx _objc_autoreleasePoolPush movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_-(LPC1_0+4)) movs r2, #0 movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_-(LPC1_0+4)) mov r4, r0 movw r0, :lower16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC1_1+4)) LPC1_0: add r1, pc movt r0, :upper16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC1_1+4)) LPC1_1: add r0, pc ldr r1, [r1] ldr r0, [r0] blx _objc_msgSend @ InlineAsm Start mov r7, r7 @ marker for objc_retainAutoreleaseReturnValue @ InlineAsm End blx _objc_retainAutoreleasedReturnValue movw r6, :lower16:(L__unnamed_cfstring_-(LPC1_2+4)) movt r6, :upper16:(L__unnamed_cfstring_-(LPC1_2+4)) LPC1_2: add r6, pc mov r5, r0 mov r1, r5 mov r0, r6 blx _NSLog mov r0, r4 blx _objc_autoreleasePoolPop mov r0, r6 mov r1, r5 blx _NSLog mov r0, r5 blx _objc_release pop {r4, r5, r6, r7, pc} 

请给我们一些ARC的掌声! 注意,我们已经意识到我们使用的数字超出了autorelease池的范围,所以它保留了numberWithInt的返回值:就像以前一样,但是这次它将释放放在bar函数的末尾,而不是之前autorelease池被popup。 这将会使我们在一些我们可能认为是正确的代码中崩溃,但实际上却有一个微妙的内存pipe理错误。

然而,ARC能够计算调用者的所有权并在使用后释放它,也就是说,它可以像C ++中的智能指针一样行为。 使用ARC,它可以摆脱autorelease,因为autorelease是非确定性的。

您将ARC与引用计数混淆在一起。 Objective-C一直依靠内存pipe理的引用计数。 ARC继续这一传统,并且简单地消除了程序员手动将适当的调用插入到-retain-release-autorelease 。 在ARC下,编译器会为您插入这些调用,但引用计数机制与以往一样保持不变。

ARC并没有消除自动释放的需要,但是在人类通常使用它的情况下,它可以避免它。