[iOS] 在C++中实现Object-C的AutoReleasePool

博客首页 » iOS 在C++中实现Object-C的AutoReleasePool

发布于 06 Feb 2015 07:38
标签 blog
Object-C中的引用计数和AutoReleasePool的确是一个解决Object所有权传递和释放的不错的办法。Object-C能做的,C/C++其实也都能做,只是需要把文法支持转换成函数/方法调用罢了。抱着这一想法,找到了以下的文章。

Stackoverflow问答

http://stackoverflow.com/questions/8235946/objective-c-autorelease-in-c-standard-way-to-control-object-lifetime

The "best practices" in the C++03 world (that is, pre-C++11) are one of two things:

Do nothing. This is essentially memory ownership by assumption/convention. If a function returns a pointer, you should know who owns it. Usually, the documentation will tell you. There is no specific syntax for ownership of memory or of transferring ownership.

This is how an unfortunately large amount of C++ code out there manages memory. It can work, so long as everyone knows what they should be doing and who is responsible for what.

Use some form of smart pointer. std::auto_ptr is odd, but that's about as lightweight as it gets in C++03. No, you can't stick them in standard containers, but it does define a specific pattern of ownership. A boost::shared_ptr is a more effective one, and is more useful in many other places.

C++11 offers std::unique_ptr, which is essentially a "fixed" auto_ptr. It relies on C++11 language features (object movement), so you can't just write one in C++03. You can store them in standard containers and everything. But you can't just pass them around. As the name suggests, they're unique: only one of them can exist which points to that object. When that unique_ptr is destroyed, it deletes the object it references.

You can transfer ownership of a unique_ptr only by giving it away. That is, you cannot share ownership. You can return ownership, which means that the caller now owns it. You can pass ownership to another function, which means that that function owns it. But no two entities can own an object through a unique_ptr.

unique_ptr would be the preferred method of handling a function like this. If the user wants to store it non-uniquely themselves, then they can release it into a std::shared_ptr (which was also adopted into C++11).

Stackoverflow问答2

http://stackoverflow.com/questions/2557562/using-apple-autorelease-pools-without-objective-c

id is a C declaration. You can simply add scope based autorelease pools to your cpp program like so:

autorelease_pool.hpp

class t_autorelease_pool {
public:
t_autorelease_pool();
~t_autorelease_pool();
private:
id d_pool; // « you may opt to preprocess this out on other platforms.
private:
t_autorelease_pool(const t_autorelease_pool&);
t_autorelease_pool& operator=(const t_autorelease_pool&);
};
autorelease_pool.mm

t_autorelease_pool::t_autorelease_pool() : d_pool([NSAutoreleasePool new]) {}
t_autorelease_pool::~t_autorelease_pool() { [this->d_pool drain]; }
In a cpp program:

void UpdateUI() {
t_autorelease_pool pool;
// your/their autoreleasing code here
}
An alternative (which can be very easy to use incorrectly) is to use the ObjC runtime directly - which would look like the following C program:

#include <objc/runtime.h>
#include <objc/message.h>

id pool = objc_msgSend(objc_getClass("NSAutoreleasePool"), sel_getUid("new"));
/* do stuff */
objc_msgSend(pool, sel_getUid("drain"));

http://blog.csdn.net/n5/article/details/7883864

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

引擎delete算法游戏工作c

想起这个话题,是因为最近在研究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的使用者们,不妨一试~


本页面的文字允许在知识共享 署名-相同方式共享 3.0协议和GNU自由文档许可证下修改和再使用,仅有一个特殊要求,请用链接方式注明文章引用出处及作者。请协助维护作者合法权益。


系列文章

文章列表

  • iOS 在C++中实现Object-C的AutoReleasePool

这篇文章对你有帮助吗,投个票吧?

rating: 0+x

留下你的评论

Add a New Comment
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License