所以考虑以下方法:
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 鼓掌!请注意,它已经意识到我们是
使用自动释放池块范围之外的数字,所以
它保留了 numberWithInt: 的返回值,就像它一样
之前,但这次将释放放在栏的末尾
函数,而不是在自动释放池弹出之前。那将
为我们节省了一些我们可能认为是的代码崩溃
正确但实际上有一个微妙的内存管理错误。