引用计数带来的一次讨论

作者: Joy___ | 来源:发表于2016-06-15 13:02 被阅读2319次

    关于 NSString 的疑问

    NSString *str = @"Joy";
    
    NSLog(@"%lu",[str retainCount]);
    NSLog(@"地址:%p",str);
    

    打印结果:

    2016-06-15 10:22:17.084 OCTestProject[2592:327118] 计数:18446744073709551615
    2016-06-15 10:22:17.085 OCTestProject[2592:327118] 地址:0x1045077d0
    

    会发现引用计数是一个很大的值,为什么?这是一个放在常量区的字符串常量,返回的结果是UINT_MAX

    关于 release 之后仍然为 1的疑问

    Student *stu = [[Student alloc] init];
    NSLog(@"%lu",[stu retainCount]);
    
    [stu release];
    NSLog(@"%lu",[stu retainCount]);
    

    打印结果:

    2016-06-15 12:07:50.608 OCTestProject[3437:361531] 1
    2016-06-15 12:07:50.609 OCTestProject[3437:361531] 1
    

    有人告诉我,是autoreleasepool的原因!!明确的说,和autoreleasepool完全没关系。

    向一个被回收的对象发送retaincount消息,输出结果不确定,如果这块内存被复用了,那么这里就会造成程序崩溃。最后一次release之后,系统知道这块内存要进行回收了,但是只是进行一个标记,并不会将retaincount减去1,也没必要这么做了。直接标记,可以减少一次内存操作,加速对对象的回收,何乐而不为 (有人对这块有疑问,可以参考玉哥的源码分析文章:http://yulingtianxia.com/blog/2015/12/06/The-Principle-of-Refenrence-Counting/

    什么对象自动加入到 autoreleasepool

    虽然在程序入口,已经帮我们加上了 autoreleasepool,但是并不是说大括号内的所有
    对象都会交给autoreleasepool来处理

    第一种

    当使用alloc/new/copy/mutableCopy开始的方法进行初始化时,会生成并持有对象(也就是不需要pool管理,系统会自动的帮他在合适位置release)

       例如: NSObject *stu = [[NSObject alloc] init]; 
    

    那么对于其他情况,例如

        id obj = [NSMutableArray array];
    

    这种情况会自动将返回值的对象注册到autorealeasepool,代码等效于:

    @autorealsepool{
        id __autorealeasing obj = [NSMutableArray array];
    }
    

    第二种

    __weak修饰符只持有对象的弱引用,而在访问引用对象的过程中,该对象可能被废弃。那么如果把对象注册到autorealeasepool中,那么在@autorealeasepool块结束之前都能确保对象的存在。

    最新的情况是weak修饰的对象不会再被加入到Pool中了,具体可参考:https://stackoverflow.com/questions/40993809/why-weak-object-will-be-added-to-autorelease-pool

    id __weak obj1 = obj0;
    NSLog(@"class=%@",[obj1 class]);
    

    对应的模拟源码为

    id __weak obj1 = obj0;
    id __autorealeasing tmp = obj1;
    NSLog(@"class=%@",[tmp class]);
    

    第三种

    id的指针或对象的指针在没有显式指定时会被附加上__autorealeasing修饰符

        + (nullable instancetype)stringWithContentsOfURL:(NSURL *)url
                                            encoding:(NSStringEncoding)enc
                                               error:(NSError **)error;
    

    等价于

       NSString *str = [NSString stringWithContentsOfURL:
                                             encoding:
                                                error:<#(NSError * _Nullable __autoreleasing * _Nullable)#>]

    相关文章

      网友评论

      • MoussyL:作者,第三种不是很明白,“id的指针或对象的指针在没有显式指定时会被附加上__autorealeasing修饰符”, id 的指针比如: NSError ** , 但是对象的指针是什么意思 ?
      • 啊哈呵:“最后一次release之后,系统知道这块内存要进行回收了,但是只是进行一个标记,并不会将retaincount减去1,也没必要这么做了。直接标记,可以减少一次内存操作,加速对对象的回收,何乐而不为” 这应该但当时是猜,现在NSObject开源了,源码完全不是这样子,最后一次也会减1,变成0释放对象。
        retainCount打印为什么还是1,是因为retainCount源码里面就是retrun “1+引用次数”
        啊哈呵:@Joy___ 嗯嗯,原来是开个中间值new.ias来做extra_rc来减一,减成溢出,然后把中间值new.ias赋值为old.ias,然后标记deallocating,然后保存StoreExclusive(&isa.bits, oldisa.bits, newisa.bits),该做的操作都做了,只是偷偷看起来没有1,看起来少了很多操作,其实是该做都做了,性能并没有省下来。
        Joy___:源码:inline uintptr_t objc_object::rootRetainCount() {
        isa_t bits = LoadExclusive(&isa.bits);
        uintptr_t rc = 1 + bits.extra_rc;
        if (bits.has_sidetable_rc) {
        rc += sidetable_getExtraRC_nolock();
        }
        return rc;
        }

        这里是 extra_rc + 1,extra_rc存储的值本来就比真正的引用计数-1 ,这里没有意外会返回正确的引用计数,至于为什么 relaease 之后,还是打印1,就是 在release 的时候会判断 extra_rc 是否已经为0,如果为 0 ,则直接去 dealloc ,而不再 -1 。
        Joy___:@啊哈呵 赞赞,求分享源码链接
      • mark666:没明白第一种不需要pool管理什么意思?不需要对象怎么能释放掉呢?
        Joy___:@mark666 :cold_sweat:那你得再多查查喽
        mark666:@Joy___ 我觉得你的理解是有问题的,首先肯定是需要把对象加到系统的自动释放池中,然后对像会变为autorelease对象,等待runloop进入休眠前统一给对象发送release消息,然后引用技术为0的对象才会销毁
        Joy___:@mark666 编译器会帮你加上release
      • 郑明明:传递链和响应链的事件对象也会加入到池子中
      • Orr:作为方法的返回值时,也会被附加上__autorealeasing修饰符
        郑明明:类方法其实也就是这个意思
      • KylinRoc:现在 ARC 不会 autorelease 弱引用对象了,而是直接 release
        Joy___:@Lion_Liu 没查:relieved:你查阅下
        Lion_Liu:@Joy___ 现在 weak 对象 ,还会放在 autoreleasePool 中吗?
        Joy___:@KylinRoc 哦哦哦:hushed:,我试验下,谢大佬
      • 沐晨__:感觉 `alloc/new/copy/mutableCopy` 这里可以写多一点,通过 `alloc/new/copy/mutableCopy` 初始化的对象是交由 ARC 处理,如果是 `[[NSObject alloc] autorelease];` 是交由 `@autoreleasepool` 来处理。
        我的理解,Autorelease 和 ARC 是两种机制,就是不知道有没理解错 :smile:
        XIAODAO:ARC是在编译阶段帮你自动在合适的位置插入必要的 retain/release/autorelease 的代码调用。我觉得两者是不同的,ARC使用autoreleasepool来实现“自动”释放
        Joy___:其实这个东西,我觉得也可以说是两种机制,😄
        沐晨__:不对,翻回了以前的笔记,不能说是两种机制

        `如果这个对象是用来返回的或者它是一个容器对象,ARC 会加入一个 autorelease 语句`

        ```
        - (Bar *)makeBar
        {
        Bar *tBar
        //...
        //... conversion code goes here
        //...
        [tBar autorelease];
        return (tBar);
        }
        ```



      • xzZZzx:请问下,关于计数为1后release会被标记而不是-1,这个有没有什么文档或者在哪里可以看到相关的证明呢?
        da27c260cc85:@Joy___ 感觉和Linux 的内存管理有关,对于内存标记inactive
        Joy___:@xzZZzx 这呀,唐巧的书中有写……也就是不会立马回收,直到下次有指针再次指向这块内存被复用……没有发现官方的文档有写……应该不会错
      • nlpjoe:可以的 :smile: convenient的构造方法会加autorelease是因为有个编译属性NS_RETURNS_NOT_RETAIN,alloc/new/copy/mutablecopy生成的是普通对象,因为编译属性是NS_RETURNS_RETAIN。
        Joy___:@周俊佐 可以
      • AidenRao:看完了,给你点个赞 :+1:
        Joy___:@饶志臻 😂 都是比较基础的内容 哈哈
      • AidenRao:先抢一楼再看

      本文标题:引用计数带来的一次讨论

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