引用计数带来的一次讨论

作者: 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)#>]

相关文章

  • 引用计数带来的一次讨论

    关于 NSString 的疑问 打印结果: 会发现引用计数是一个很大的值,为什么?这是一个放在常量区的字符串常量...

  • iOS引用计数讨论

    首先看下面这段代码 打印结果为: -1 1 我们忽略掉那个[str retain], 因为retain操作只会增加...

  • GC算法

    引用计数 每个对象有一个引用计数,当对象被多引用一次,引用计数加一,当引用被释放,引用计数减一,当引用计数为零,则...

  • 对象已死与垃圾回收算法

    1. 对象已死 1.1 引用计数算法 引用计数算法是指添加一个引用计数器,每被引用一次,计数器就加一,当引用失效时...

  • 02 JVM垃圾回收机制

    1 对象回收判断 1.1 引用计数法 每个对象有个引用计数器,当对象被引用一次则计数器加1,当对象引用失效一次则计...

  • 经典面试题14 - 垃圾回收和自动引用计数

    问题自动引用计数(ARC) 和 垃圾回收(GC)有什么区别? 解答在讨论自动引用计数(ARC) 和 垃圾回收(GC...

  • Java--垃圾回收算法

    1.引用计数法  堆中每个对象都有一个引用计数。被引用一次,计数加1.被引用变量值变为null,则计数减1,直到计...

  • JVM学习(5)垃圾回收算法

    一.概述: 主要讨论:引用计数法,标记压缩法,标记清除法,复制算法和分代分区的思想。 二.引用计数法(Refere...

  • 深入理解java虚拟机读书笔记-垃圾收集算法

    判断对象是否可被回收: 1.引用计数法:给对象添加一个引用计数器,每被引用一次,计数器+1,引用失效时,计数器-1...

  • 垃圾收集器 与内存分配

    垃圾回收器与内存回收策略 对象已死? 引用计数算法给对象加一个计数器,引用一次计数器加1,引用失效时,计数器减1。...

网友评论

  • 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
  • 郑明明:传递链和响应链的事件对象也会加入到池子中
  • ccd098ddf231:作为方法的返回值时,也会被附加上__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