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

2018-08-10  本文已影响70人  9a957efaf40a

以下的测试均基于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
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行。

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

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

上一篇 下一篇

猜你喜欢

热点阅读