本文由 简悦 SimpRead转码, 原文地址 www.galloway.me.uk
继我第一篇关于 ARC 如何在引擎盖下工作的文章之后,我想我应该分享......。
继我第一篇关于ARC 如何在引擎盖下工作的文章之后,我想再分享一个我觉得有趣的小片段。这一次,我想知道当你从数组中取出一个对象并从一个方法中返回它时会发生什么。在 ARC 之前,您会保留对象,然后自动释放返回。有了 ARC,我们可以摆脱这些内存管理调用,但感觉就是不对。因此,我决定检查一下 ARC 的做法是否正确。
请看这个类:
#import <Foundation/Foundation.h>
@interface ClassA : NSObject
@property (nonatomic, strong) NSMutableArray *array;
@end
@implementation ClassA
@synthesize array;
- (id)popObject {
id lastObject = [array lastObject];
if (lastObject) {
[array removeLastObject];
}
return lastObject;
}
@end
在非 ARC 环境中,调用 removeLastObject 会释放数组中的对象,如果这是最后一次引用,则会 dealloc 该对象,这意味着我们返回的是一个死对象。因此,我们将保留 lastObject,然后以自动释放的方式返回。
但这让我很害怕,尽管我很清楚 ARC 应该完成它的工作,但我还是没有这么做。我之所以这么想,是因为我天真地以为 ARC 会逐行解析该方法。如果它这样做了,那么我认为它不一定会添加保留,因为当我们得到最后一个对象的引用时,ARC 并不知道它需要添加保留,因为它为什么一定要这样做呢?
这就是我错的地方。显然,ARC 的做法是,一旦我们得到了对象的引用,它就会添加一个 retain,然后当该变量离开作用域时,它就会添加一个 release,或者在我们的例子中,因为我们正在返回它,而且方法名不是以 new 或 copy 开头的,所以它会自动释放它。
让我们看看编译后的代码:
.thumb_func "-[ClassA popObject]"
"-[ClassA popObject]":
push {r4, r5, r6, r7, lr}
movw r6, :lower16:(_OBJC_IVAR_$_ClassA.array-(LPC0_0+4))
mov r4, r0
movt r6, :upper16:(_OBJC_IVAR_$_ClassA.array-(LPC0_0+4))
movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_-(LPC0_1+4))
LPC0_0:
add r6, pc
movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_-(LPC0_1+4))
LPC0_1:
add r1, pc
add r7, sp, #12
ldr r0, [r6]
ldr r1, [r1]
ldr r0, [r4, r0]
blx _objc_msgSend
@ InlineAsm Start
mov r7, r7 @ marker for objc_retainAutoreleaseReturnValue
@ InlineAsm End
blx _objc_retainAutoreleasedReturnValue
mov r5, r0
cbz r5, LBB0_2
movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC0_2+4))
movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC0_2+4))
ldr r0, [r6]
LPC0_2:
add r1, pc
ldr r1, [r1]
ldr r0, [r4, r0]
blx _objc_msgSend
LBB0_2:
mov r0, r5
blx _objc_autoreleaseReturnValue
pop {r4, r5, r6, r7, pc}
好了,就是这样。ARC 为我们做了一件好事。它所做的实际上是添加了一个对 objc_retainAutoreleaseReturnValue 的调用,这意味着它已经注意到需要保留一个自动释放返回的值,这肯定是 ARC 的一种优化,它只会从自动释放池中提取一次对象,而不是真正执行一次保留。然后,在方法结束时,它会调用 objc_autoreleaseReturnValue 来自动释放我们返回的值。
这只是如何查看 ARC 在引擎盖下所做工作的另一个例子。我使用 ARC 越多,就越能意识到它的作用。它使代码不易出现内存管理错误,并允许进行优化,比如这里显示的保留自动释放的返回值。