所以考虑以下方法:
void foo() {
@autoreleasepool {
NSNumber *number = [NSNumber numberWithInt:0];
NSLog(@"number = %p", number);
}
}
当然,这完全是人为的,但它应该让我们看看发生了什么。在非 ARC 领域,我们假设该数字将在 numberWithInt: 内分配并返回自动释放。所以当自动释放池下一次耗尽时,它将被释放。所以让我们看看是否发生了这种情况(像往常一样,这是 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}
嗯,是。这正是正在发生的事情。我们可以看到推送自动释放池的调用,然后是对 numberWithInt: 的调用,然后是对弹出自动释放池的调用。正是我们所期望的。现在让我们看看在 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 已经为我们确定它并不真正需要担心现有的自动释放池,因为它可以简单地告诉自动释放不会发生(通过调用 objc_retainAutoreleasedReturnValue)然后稍后释放对象本身。这是可取的,因为这意味着不必发生自动释放逻辑。
请注意,仍然需要推送和弹出自动释放池,因为 ARC 无法知道对 numberWithInt: 和 NSLog 的调用中发生了什么,以了解对象是否会被放入池中。如果它确实知道他们没有自动释放任何东西,那么它实际上可以摆脱推送和弹出。也许这种逻辑会出现在未来的版本中,尽管我不太确定它的语义是如何工作的。
现在让我们考虑另一个例子,我们想在自动释放池块范围之外使用数字。这应该向我们展示为什么使用 ARC 是一个奇迹。考虑以下代码:
void bar() {
NSNumber *number;
@autoreleasepool {
number = [NSNumber numberWithInt:0];
NSLog(@"number = %p", number);
}
NSLog(@"number = %p", number);
}
您可能(正确地)认为这会导致问题,即使它看起来完全无害。这是一个问题,因为数字将在自动释放池块内分配,当自动释放池弹出时将被释放,但在释放后会被使用。哦哦!让我们通过在不启用 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 鼓掌!请注意,我们意识到我们正在使用自动释放池块范围之外的 number,因此它保留了 numberWithInt: 的返回值,就像它之前所做的一样,但是这一次它将释放放置在 bar 函数的末尾而不是之前自动释放池被弹出。这将避免我们在一些我们可能认为是正确的但实际上有一个微妙的内存管理错误的代码中崩溃。