zoukankan      html  css  js  c++  java
  • 引用计数

    《iOS开发进阶》读书笔记


    引用计数

    引用计数可以有效地管理对象的生命周期。当我们创建新对象的时候,他的引用计数为1,当有一个指针指向这个对象时,我们将其引用计数加1,当某个指针不再指向这个对象时,我们将其引用计数减1.当对象的引用计数变为0时,说明这个对象不再被任何指针指向了,这个时候我们就可以将对象销毁,回收内存。

    我们为什么需要引用计数?

    假如对象A生成一个对象M,需要调用对象B的某一个方法,把对象M作为参数传递过去。在没有引用技术的情况下,一般内存管理的原则是“谁申请,谁释放”,那么对象A就需要在对象B不再需要M的时候将对象M销毁。但对象B可能致死临时用一下对象M,也可能觉得M很重要将他设置为一个成员变量,在这种情况下,什么时候释放对象M成了一个难题。

    有一个暴力的方法就是对象A调用完对象B之后立马销毁参数对象M,然后对象B需要另外将参数复制一份,生成另一个对象M2,然后自己管理对象M2的生命周期。但是这种做法有一个很大的问题就是他带来更多的内存申请、复制、释放的工作。本来一个可以服用的对象,因为不方便管理他的生命期就简单的把他销毁然后又重新构造一份,实在是太影响性能。

    我们还有一个方法,就是对象A构造完对象M之后,始终不销毁对象M,由对象B来完成对象M的销毁工作。如果对象B需要长时间使用对象M,就不销毁他,如果只是临时用一下就可以用完后马上销毁。这种做法看似很好的解决了对象复制的问题,但是他强烈依赖于A、B两个对象的配合,代码维护者需要明确记住这种编程约定。而且,由于对象M申请在A中,释放在对象B中,使得它的内存管理代码分散在不同对象中,管理起来也非常费劲。如果这个时候情况再复杂一点,例如,对象B需要再向对象C传递对象M,那么这个对象在对象C中又不能让对象C管理。所以这种方式带来的复杂性更大,更不可取。

     

     

    所以引用计数解决了这个问题,在传递参数M的过程中,哪些对象需要长时间使用这个对象,就把他的引用计数加1,使用完了之后再把引用计数减1.所有对象都遵守这个规则的话,对象的生命期管理就可以完全交给引用计数了。

     

    不要向已经释放的对象发送消息

    当我们想去打印查看引用计数时,我们会尝试使用如下代码(非ARC环境下):

    但是我们得到的结果却是这样的:

    我们注意到最后一次输出引用计数并没有变成0.这是为什么?因为该对象的内存已经被回收,而我们向一个已经被回收的对象发了一个retainCount消息,所以他的输出结果应该是不确定的,如果该对象占用的内存被复用,那么就有可能造成程序异常崩溃。

    那么为什么在这个对象被回收之后,这个不确定的值是1而不是0呢?这是因为当最后一次执行release时,系统知道马上就要回收内存了,就没有必要再将retainCount减1了,因为不管减不减1,该对象都肯定会被回收,而对象呗回收后,他的所有内存区域,包括retainCount值也变得没有意义。不将这个值从1变成0,可以减少一次内存的操作,加速对象的回收。

     

    循环引用的问题

    引用计数这种管理内存的方式虽然很简单,但是有一个比较大的瑕疵,即它不能很好地解决循环引用的问题。对象A和对象B,相互引用对方作为自己的成员变量,只有自己销毁时才会将成员变量引用计数减1.因为对象A的销毁依赖于对象B的销毁,对象B的销毁又依赖于对象A的销毁,这样就造成了循环引用的问题。

    解决循环引用的问题主要有两个办法。

    第一个办法就是明确知道这里会有循环引用,在合理的位置断开环中的一个引用,使得对象得以回收。

    第二个方法是弱引用的方法。这个方法更为常用。弱引用虽然持有对象但是并不增加引用计数,这样就避免了循环引用的产生。iOS开发中的delegate常用弱引用方式。

     

    不知道引用计数原理过度依赖ARC

    过度依赖ARC的iOS新人们,由于不知道引用计数,他们的问题主要体现在两个方面:

    1.过度使用block之后无法解决循环引用的问题。

    2.遇到底层Core Foundation对象,需要手工管理他们的引用计数时,显得一筹莫展。Core Foundation对象的内存管理:

     
    对于CFRetain和CFRelease两种方法,可以理解为和Objective-C对象的retain和release方法等价。
    在ARC下,还有一个问题需要解决。我们有时需要将一个Core Foundation对象转换成一个Objective-C对象,这时候我们需要告诉编译器,转换过程中的引用计数如何调整。这就引入了与bridge相关的关键字,以下是这些关键字的说明:
    1.__bridge:只做类型转换,不修改相关对象的引用计数,原来的Core Foundation对象在不用时,需要调用CFRelease方法。
    2.__bridge_retained:类型转换后,将相关对象的引用计数加1,原来的Core Foundation对象在不用时,需要调用CFRelease方法。
    3.__bridge_transfer:类型转换后,将该对象的引用计数交给ARC管理,Core Foundation对象在不用时,不需要调用CFRelease方法。
  • 相关阅读:
    echarts使用
    Nutch插件系统
    linux命令总结
    linux命令行快捷键
    每日一笔
    Hadoop参数调优
    rsync用于同步目录
    hadoop遇到的问题(汇总)
    linux历史命令
    hadoop 编译代码及运行
  • 原文地址:https://www.cnblogs.com/sunjianfei/p/7061099.html
Copyright © 2011-2022 走看看