Objective-C中的自动释放池

自动释放池块@autoreleasepool

自动释放池块在MRC和ARC下都可以使用。在MARC下,为了将自动释放池块内部的变量放入自动释放池,需要手动调用autorelease方法;在ARC下,只能通过声明变量为__autoreleasing来达到目的,而不是自动释放池块内部的所有变量,都会进入自动释放池。

加入在ARC下,有如下代码:

@autoreleasepool {
    X    *x = [[X alloc] init];
}

如果在Xcode控制台使用_objc_autoreleasePoolPrint()方法查看,会发现自动释放池没有任何变量。

但是如果我们进行如下调用:

@autoreleasepool {
    X    *x = [X create]; //create返回X类型对象
}

然后使用_objc_autoreleasePoolPrint()方法查看,会发现自动释放池里面持有X对象:

(lldb) po _objc_autoreleasePoolPrint()
objc[4574]: ##############
objc[4574]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[4574]: 2 releases pending.
objc[4574]: [0x10100c000]  ................  PAGE  (hot) (cold)
objc[4574]: [0x10100c038]  ################  POOL 0x10100c038
objc[4574]: [0x10100c040]       0x100509270  X
objc[4574]: ##############
0x70a40db675eb00e6

这个持有的X对象是因为调用的create方法不是以alloc/new/init/copy/mutableCopy开头命名,因此编译器在create方法返回前,会自动将返回的变量放入到自动释放池,而不是因为在自动释放池块内部声明了变量x导致的。

如果我们将变量x加上__autoreleasing属性,就看的很明白了,对象X在自动释放池中放入了两次:

(lldb) po _objc_autoreleasePoolPrint()
objc[4667]: ##############
objc[4667]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[4667]: 3 releases pending.
objc[4667]: [0x103803000]  ................  PAGE  (hot) (cold)
objc[4667]: [0x103803038]  ################  POOL 0x103803038
objc[4667]: [0x103803040]       0x102928700  X
objc[4667]: [0x103803048]       0x102928700  X
objc[4667]: ##############
0xb8c9f2d34f6300b0

 

 

将变量放入到自动释放池中,不会增加变量的引用计数

要看清这个问题,可以在MRC下将变量多次调用autorelease方法,然后使用CFGetRetainCount方法在Xcode控制台进行查看:

//MRC

@autoreleasepool {
    X *x = [[X alloc] init];
    [x autorelease];
    [x autorelease];
    [x autorelease];
}

控制台查看的结果如下,引用技术为1:

(lldb) p CFGetRetainCount((__bridge CFTypeRef)x)
(CFIndex) $0 = 1
(lldb)

在ARC下,结论依然不变,但是更具迷惑性。

//ARC

@autoreleasepool {
    X __autoreleasing *x = [[X alloc] init];
}

使用Xcode控制台查看,会发现变量X被放入了自动释放池,并且引用计数为1,符合预期:

(lldb) po _objc_autoreleasePoolPrint()
objc[4961]: ##############
objc[4961]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[4961]: 2 releases pending.
objc[4961]: [0x104003000]  ................  PAGE  (hot) (cold)
objc[4961]: [0x104003038]  ################  POOL 0x104003038
objc[4961]: [0x104003040]       0x10301c820  X
objc[4961]: ##############
0x3d01f52ffddf001a

(lldb) p CFGetRetainCount((__bridge CFTypeRef)x)
(CFIndex) $1 = 1
(lldb) 

再看下面的代码:

//ARC

@autoreleasepool {
   X __autoreleasing *x = [X create];//create返回X类型对象
}

在Xcode控制台查看,会发现x变量被放入了两次自动释放池,这是符合预期的,但是x变量的引用计数却变成了2,给人的感觉是没放入一次自动释放池,计数就加1:

(lldb) po _objc_autoreleasePoolPrint()
objc[5052]: ##############
objc[5052]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[5052]: 3 releases pending.
objc[5052]: [0x106003000]  ................  PAGE  (hot) (cold)
objc[5052]: [0x106003038]  ################  POOL 0x106003038
objc[5052]: [0x106003040]       0x1005176a0  X
objc[5052]: [0x106003048]       0x1005176a0  X
objc[5052]: ##############
0x4c2912a2a0dd00bc

(lldb) p CFGetRetainCount((__bridge CFTypeRef)x)
(CFIndex) $1 = 2
(lldb) 

但是如果我们使用clang编译器的-S选项,将源码转换成汇编码,就会发现其中的奥秘:

.loc    1 41 32                 ## autorelease.m:41:32
    movq    L_OBJC_CLASSLIST_REFERENCES_$_(%rip), %rsi
    movq    L_OBJC_SELECTOR_REFERENCES_.11(%rip), %rcx
    movq    %rsi, %rdi
    movq    %rcx, %rsi
    movq    %rax, -32(%rbp)         ## 8-byte Spill
    callq    *_objc_msgSend@GOTPCREL(%rip)
    .loc    1 41 28 is_stmt 0       ## autorelease.m:41:28  41行源码就是X __autoreleasing *x = [X create];
    movq    %rax, %rdi
    callq    _objc_retainAutoreleasedReturnValue    ##这个Retain方法将返回的值被持有了一次,所以引用计数变成了2
    movq    %rax, %rdi
    callq    _objc_autorelease
    leaq    L__unnamed_cfstring_.13(%rip), %rcx
    movq    %rax, -24(%rbp)


在看下面这种情况,和上面的例子的效果一样:

//ARC
@autoreleasepool {
    
        X __autoreleasing *x1 = [X create];
        X __autoreleasing *x2 = x1; 
        
 
    }

在Xcode中查看输出,自动释放池里面有3个变量,符合预期,引用计数为3,符合预期:

(lldb) po _objc_autoreleasePoolPrint()
objc[5401]: ##############
objc[5401]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[5401]: 4 releases pending.
objc[5401]: [0x102004000]  ................  PAGE  (hot) (cold)
objc[5401]: [0x102004038]  ################  POOL 0x102004038
objc[5401]: [0x102004040]       0x100708950  X
objc[5401]: [0x102004048]       0x100708950  X
objc[5401]: [0x102004050]       0x100708950  X
objc[5401]: ##############
0xfbeaa82f2b80004a

(lldb) p CFGetRetainCount((__bridge CFTypeRef)x1)
(CFIndex) $1 = 3
(lldb) 

查看汇编码可知:

.loc    1 41 28 is_stmt 0       ## autorelease.m:41:28  调用[X create]源码处
    movq    %rax, %rdi
    callq    _objc_retainAutoreleasedReturnValue  ##第一处Retain方法
    movq    %rax, %rdi
    callq    _objc_autorelease
    movq    %rax, -24(%rbp)
    .loc    1 42 28 is_stmt 1       ## autorelease.m:42:28  复制x2 = x1处
    movq    -24(%rbp), %rax
    movq    %rax, %rdi
    callq    _objc_retainAutorelease  #第二处retain方法,这个方法retain的同时,将变量放入自动释放池
    leaq    L__unnamed_cfstring_.13(%rip), %rcx
    movq    %rax, -32(%rbp)

 

posted @ 2019-07-07 18:30  chaoguo1234  阅读(432)  评论(0编辑  收藏  举报