从引用计数,auto release pool 到 shared ptr

    想起这个话题,是因为最近在研究cocos2d-x,其内存管理机制使用c++模拟了Obj-C的auto release pool机制。先从头说起,这是一种基于引用计数的技术。

我最早接触引用计数,是学习研究irrlicht引擎,当时的感觉是,必须弄清楚每个返回对象的函数,是否grab了对象,虽然irrlicht引擎自己是按照命名规则给予用户提示的,但是别人写的方法可就苦恼了。


 纯粹引用计数的主要问题是,必须很小心的增加和减少计数。主要的内存泄露问题都来自于漏了release。  特别是有一种情况:某某类负责产生了一个对象,但是需要使用者去释放,由于使用者并不需要在他的生命周期内保留这个对象,很可能忘了去release这个对象。怎么办呢?于是有了所谓的“自动释放池”。这个技术是Obj-C所有的,cocos2d-x使用c++模拟了。简单的思想是,负责产生对象的类,在产生对象后把这个对象加入到一个数组中(也就是所谓的auto release pool),在这一帧结束的时候(或者该pool被pop的时候),auto release pool会对其中所有的对象执行一次release,即引用减1。如果某对象没有被使用者retain (引用加1),引用就会被减为0,因此被释放掉。在cocos2d-x中(其实Obj-C也是差不多),会在每次游戏循环时自动pop一次auto release pool,将所有没人管的对象释放掉。默认情况下,系统中只有一个auto release pool,但是如果用户想提前释放某些对象,可以自己push上新的pool,然后在合适的时机pop他。这个和Obj-C的用法也是很像的。那么,从这个过程看,auto release pool解决了什么问题呢? 其实他的作用就是免除了对象指针在离开作用域时,对对象减少引用计数的工作。区别在于,他不是在离开作用域时立即release,而是在一帧结束时统一release。


好了,有了auto release pool,事情简单了很多:

1)如果你retain或new了对象,你就负责release他。

2)如果你new了对象给别人用,你自己不知道别人啥时候用完。你就把他放到auto release pool中(obj->autoRelease(); ),如果别人只是临时用一下他,auto release pool会释放对象。如果别人要较长期的使用它,他需要按第一条规则做,retain对象并在不需要对象时release。


似乎这样很不错了,但再仔细想想,我们还是要retain / release对象,我们还是不能忘记release,这和new了对象不能忘记delete没什么区别。使用引用计数的好处是可以很容易的共享对象所有权(当然要防止交叉引用的问题),并且在使用了auto release pool后,连“临时对象”的释放也自动化了。可是出来混总是要还的,retain了就要release :)


当然解决办法总是有的,shared ptr 就可以搞定。shared ptr 是一种smart ptr,具体实现不罗嗦了,核心思想是每个shared ptr实例都会持有对象的一个引用(当然这不是绝对的,因为允许有不引用对象的shared ptr存在)。shared ptr被赋值时会减去当前对象的引用(如果已经引用了某对象),并增加新对象的引用。一个对象可能被存在于很多地方不同作用域中的shared ptr所引用,咋一看这挺乱的,其实理解起来很简单,只要这些ptr都销毁了,或者主动释放了引用,这个对象就会被自动释放。而其中,最关键之处在于shared ptr都是以一个对象存在的(而不是一个指针)。无论他是某某对象的成员,或者是在一个函数的栈上产生,他是一个对象而不是一个指针,所以他离开作用域时自动销毁,所以你就不用去release什么引用计数了。

回到auto release pool解决的问题,如果使用shared ptr,负责产生对象的类只需要返回这个对象的shared ptr即可,而自己不用保存该对象。如果对象使用者只是临时用一下这个对象,那用完就好了,因为shared ptr在离开作用域时自动销毁了对象。如果对象使用者需要较长时间的使用对象,那么他就用一个shared ptr来保存这个对象,如果这个对象只需要在他自己释放时释放,这个shared ptr就可以作为他的数据成员,从而不需要主动释放。如果需要提前释放对象,可以清空这个shared ptr。


可以看到,shared ptr可以完成auto release pool的功能,并且不需要维护一个pool,区别在于“临时对象”是不用的时候立刻释放的,不需要等到帧结束时。这儿不讨论两种方式在效率和内存方面的优劣,使用shared ptr会更优雅一些,并且更容易使用。当然,说到效率,智能指针肯定要比原生指针要弱,不过如果真的在某某算法里面,需要大量的指针操作,完全可以从智能指针里面get出raw pointer来使用。其他的情况,好吧,这么省事的东西总要有些代价吧,况且根据28原理,先去解决其他问题吧。


回来再说cocos2d-x,其实在其上面实现shared ptr是很容易的,因为CCObject本身已经有了管理引用计数的功能,因此自己实现一个shared ptr的类模板,去管理CCObject类体系中的类也是很轻松的。当然如果你对boost有爱,使用他的非侵入式智能指针 intrusive_ptr 也是可以的。我觉得cocos2d-x的使用者们,不妨一试~






更多详细信息请查看 java教程网 http://www.itchm.com/forum-59-1.html

猜你喜欢

转载自hongqiang.iteye.com/blog/1637546