美文网首页
内存管理之函数参数的__strong、引用计数问题

内存管理之函数参数的__strong、引用计数问题

作者: 9a957efaf40a | 来源:发表于2018-08-10 11:05 被阅读70次

    以下的测试均基于ARC

    1.__strong修饰符

    在如下函数中:

    - (void)xx {
        Son *son = [[Son alloc] init];
        Son *s1 = son;
        NSLog(@"%ld",(long)CFGetRetainCount((__bridge CFTypeRef)(son)));
    }
    

    son的引用计数是多少?

    2018-08-10 09:48:12.275410 内存管理[1597:362731] 2
    

    这是因为s1默认为__strong修饰,则编译器自动添加retain函数,底层调用objc_retain函数,增加了对象的引用计数。相关的汇编代码如下:

    4DB6D79F-1721-4D54-92D1-A5EB298C2EA5.png

    第一个objc_msgSendalloc,第二个objc_msgSendinit,下面依次是objc_retain和打印时调用的函数CFGetRetainCount

    2.__weak修饰符

    如果添加了__weak会发生什么?

    - (void)xx {
        Son *son = [[Son alloc] init];
        __weak Son *s1 = son;
        NSLog(@"%ld",(long)CFGetRetainCount((__bridge CFTypeRef)(son)));
    }
    

    结果为:

    2018-08-10 09:58:20.115245 内存管理[1606:364519] 1
    

    相关汇编如下:


    B0F7DFC2-EAC4-410C-878B-2725A6E927BC.png

    不再调用objc_retain,取而代之的是objc_initWeak

    3.参数的引用计数

    如下函数中:

    void test(Son *s){
        NSLog(@"%ld",(long)CFGetRetainCount((__bridge CFTypeRef)(s)));
    }
    int main(int argc, char * argv[]) {
        Son *s = [[Son alloc] init];
        test(s);
    }
    

    test(Son *s)函数中,s指针不像我们在示例1和示例2中定义的那样,没有明确的__strong__weak修饰符,甚至都无法知道s指针是如何定义的。那么,s会对传递的对象增加引用计数吗?

    结果如下:

    2018-08-10 10:05:07.315869 内存管理[1611:365666] 2
    

    显然,作为参数传递的对象,进入函数后,会增加一次引用计数。

    那么这一切是怎么做的?

    7CCF660C-EA78-4967-8219-CC900AF65D20.png
    • 1~3行为拉伸栈空间和保护寄存器,我们不讨论。
    • 4行将sp中的值加上0x20,然后赋值给x29sp中的值为0x000000016fd6fa10。则x290x000000016fd6fa30
    • 5行将x29的值减去0x8,然后赋值给x8。则x8的值为0x000000016fd6fa28
    • 6行将x9中的值置为0x0
    • 7行,取x29的值减去0x8后的值作为地址,将x9中的值0x0写入到该地址的内存中。(结合第5行,也就是说,如果x8的值做为地址,该地址的值的前8个字节(一个指针的宽度)为空。这个转化很重要!!!)
    • 8行,取sp的值加上0x10后的值作为地址,将x0的值写入到该地址的内存空间中。x0寄存器保存函数调用时的第一个参数,那么在这里,它的值就是参数s
      705FB3F3-6412-49DE-8706-1AF8BCD8A872.png
    • 9行,将x8的值给x0。此时x0的值为0x000000016fd6fa28
    • 10行,将sp的值加上0x10后的值作为地址,从该地址的内存空间中取出值赋给x1。此时x1的值为s指针,指向Son对象。
    • 11行,调用objc_storeStrong函数。
    void objc_storeStrong(id *location, id obj)
    {
        id prev = *location;
        if (obj == prev) {
            return;
        }
        objc_retain(obj);
        *location = obj;
        objc_release(prev);
    }
    

    可以看到,该函数需要两个参数。
    第一个参数为二级指针。而此时x00x000000016fd6fa28,上面我们说过,如果这个值作为地址,该地址的值的前8个字节(一个指针的宽度)是空。那么也就是说,该函数中id prev为空。
    第二个参数为OC对象。此时x1的值为指针s
    可以看到,obj != prev,因此执行objc_retain(obj)Son对象的引用计数加1,为2。
    此时0x000000016fd6fa28作为地址,值的前8个字节不再是空,而是指向Son对象的指针。

    汇编中间打印部分我们不讨论,现在,跳到第20行。

    • 20行,x8置空。
    • 21行,取x29的值减去0x8后的值,赋给x9x90x000000016fd6fa28。此时该值作为地址,该地址的值的前8个字节,在经过第11行的函数objc_storeStrong后,已经不再是空,而是一个指针,指向Son对象。
    • 22~23行,x00x000000016fd6fa28x10x0
    • 24行,调用objc_storeStrong函数。

    此时第一个参数为二级指针,取值为id prev,prev为指向Son对象的指针。
    第二个参数obj为空。
    可以看到,obj != prev,因此执行objc_retain(nil),然后0x000000016fd6fa28作为地址,该地址的值的前8个字节置为空,最后objc_release(prev),即Son对象的引用计数减1。

    结论 : OC对象参数在进入函数时,会增加引用计数,在函数结束时,会减少引用计数。

    相关文章

      网友评论

          本文标题:内存管理之函数参数的__strong、引用计数问题

          本文链接:https://www.haomeiwen.com/subject/cexkbftx.html