深入理解 autorelease

深入理解 autorelease

先上一段代码,分别实现了包含 autorelease pool 不包含 autorelease pool 的函数

1
2
3
4
5
6
7
8
9
void autoreleasepool() {
@autoreleasepool {
NSObject *a = [[NSObject alloc] init];
}
}

void noautoreleasepool() {
NSObject *a = [[NSObject alloc] init];
}

编译后再用hopper disassembler 反编译,然后看 autorelasepool函数的汇编代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 _autoreleasepool:
0000000100006b80 stp x29, x30, [sp, #0xfffffff0]! ; XREF=_main+24
0000000100006b84 mov x29, sp
0000000100006b88 sub sp, sp, #0x10
0000000100006b8c bl imp___stubs__objc_autoreleasePoolPush
0000000100006b90 adrp x8, #0x100008000 ; imp___got_dyld_stub_binder
0000000100006b94 add x8, x8, #0xc30 ; @selector(alloc)
0000000100006b98 adrp x9, #0x100008000 ; imp___got_dyld_stub_binder
0000000100006b9c add x9, x9, #0xc40 ; objc_cls_ref_NSObject
0000000100006ba0 ldr x9, [x9] ; objc_cls_ref_NSObject
0000000100006ba4 ldr x1, [x8] ; @selector(alloc)
0000000100006ba8 str x0, [sp]
0000000100006bac mov x0, x9
0000000100006bb0 bl imp___stubs__objc_msgSend
0000000100006bb4 adrp x8, #0x100008000 ; imp___got_dyld_stub_binder
0000000100006bb8 add x8, x8, #0xc38 ; @selector(init)
0000000100006bbc ldr x1, [x8] ; @selector(init)
0000000100006bc0 bl imp___stubs__objc_msgSend
0000000100006bc4 movz x8, #0x0
0000000100006bc8 add x9, sp, #0x8
0000000100006bcc str x0, [sp, #0x8]
0000000100006bd0 mov x0, x9
0000000100006bd4 mov x1, x8
0000000100006bd8 bl imp___stubs__objc_storeStrong
0000000100006bdc ldr x0, [sp]
0000000100006be0 bl imp___stubs__objc_autoreleasePoolPop
0000000100006be4 mov sp, x29
0000000100006be8 ldp x29, x30, [sp], #0x10
0000000100006bec ret
; endp

再看 noautoreleasepool 函数的反汇编代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
   _noautoreleasepool:
0000000100006bf0 stp x29, x30, [sp, #0xfffffff0]! ; XREF=_main+28
0000000100006bf4 mov x29, sp
0000000100006bf8 sub sp, sp, #0x10
0000000100006bfc adrp x8, #0x100008000 ; imp___got_dyld_stub_binder
0000000100006c00 add x8, x8, #0xc30 ; @selector(alloc)
0000000100006c04 adrp x9, #0x100008000 ; imp___got_dyld_stub_binder
0000000100006c08 add x9, x9, #0xc40 ; objc_cls_ref_NSObject
0000000100006c0c ldr x9, [x9] ; objc_cls_ref_NSObject
0000000100006c10 ldr x1, [x8] ; @selector(alloc)
0000000100006c14 mov x0, x9
0000000100006c18 bl imp___stubs__objc_msgSend
0000000100006c1c adrp x8, #0x100008000 ; imp___got_dyld_stub_binder
0000000100006c20 add x8, x8, #0xc38 ; @selector(init)
0000000100006c24 ldr x1, [x8] ; @selector(init)
0000000100006c28 bl imp___stubs__objc_msgSend
0000000100006c2c movz x8, #0x0
0000000100006c30 add x9, sp, #0x8
0000000100006c34 str x0, [sp, #0x8]
0000000100006c38 mov x0, x9
0000000100006c3c mov x1, x8
0000000100006c40 bl imp___stubs__objc_storeStrong
0000000100006c44 mov sp, x29
0000000100006c48 ldp x29, x30, [sp], #0x10
0000000100006c4c ret
; endp

两者的区别是 autorelease 版本多了如下内容

1
2
3
4
5
6
7
0000000100006b8c         bl         imp___stubs__objc_autoreleasePoolPush  ; 调用 objc_autoreleasePoolPush方法
0000000100006ba8 str x0, [sp] ; 把objc_autoreleasePoolPush的返回值入栈

......

0000000100006bdc ldr x0, [sp] ; 把之前入栈的返回值出栈
0000000100006be0 bl imp___stubs__objc_autoreleasePoolPop ; 调用objc_autoreleasePoolPop方法

发现了和预想不一样的地方,按照常理,在 autoreleasepool 里面的对象初始化的时候,似乎要调用-[NSObject autorelease] 来让他自动释放,然而,并没有。

看看 objc 的源码 objc_autoreleasePoolPushobjc_autoreleasePoolPop 的实现。

objc_autoreleasePoolPush

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
void *
objc_autoreleasePoolPush(void)
{
if (UseGC) return nil;
return AutoreleasePoolPage::push();
}

static inline void *push()
{
id *dest;
if (DebugPoolAllocation) {
// Each autorelease pool starts on a new pool page.
dest = autoreleaseNewPage(POOL_SENTINEL);
} else {
dest = autoreleaseFast(POOL_SENTINEL);
}
assert(*dest == POOL_SENTINEL);
return dest;
}

static inline id *autoreleaseFast(id obj)
{
AutoreleasePoolPage *page = hotPage();
if (page && !page->full()) {
return page->add(obj);
} else if (page) {
return autoreleaseFullPage(obj, page);
} else {
return autoreleaseNoPage(obj);
}
}

static __attribute__((noinline))
id *autoreleaseNoPage(id obj)
{
// No pool in place.
assert(!hotPage());

if (obj != POOL_SENTINEL && DebugMissingPools) {
// We are pushing an object with no pool in place,
// and no-pool debugging was requested by environment.
_objc_inform("MISSING POOLS: Object %p of class %s "
"autoreleased with no pool in place - "
"just leaking - break on "
"objc_autoreleaseNoPool() to debug",
(void*)obj, object_getClassName(obj));
objc_autoreleaseNoPool(obj);
return nil;
}

// Install the first page.
AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
setHotPage(page);

// Push an autorelease pool boundary if it wasn't already requested.
if (obj != POOL_SENTINEL) {
page->add(POOL_SENTINEL);
}

// Push the requested object.
return page->add(obj);
}

push 的主要作用是选一个/建一个autoreleasePoolPage ,设定为hotpage备用,流程图如下

objc_autoreleasePoolPop

1

作者

shouyi.www

发布于

2019-12-04

更新于

2025-01-30

许可协议

评论

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×