zoukankan      html  css  js  c++  java
  • 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)
  • 相关阅读:
    openCV中cvSnakeImage()函数代码分析
    程序所有重构,升级的目标(备注,更新)
    基础总结篇之中的一个:Activity生命周期
    Tomcat全攻略
    VRRP协议具体解释
    二叉树三种遍历(递归以及非递归实现)
    AssertValid函数学习
    java定时器的使用(Timer)
    循环队列
    使用Heartbeat实现双机热备
  • 原文地址:https://www.cnblogs.com/chaoguo1234/p/11147139.html
Copyright © 2011-2022 走看看