cocos2dx里的所有节点对象都继承自Ref
下面是Ref的源代码核心部分摘录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class Ref { public: void retain(); void release(); Ref * autorelease(); unsigned int getReferenceCount() const; protected: Ref(); public: virtual ~Ref(); protected: unsigned int _referenceCount; friend class AutoreleasePool; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| Ref::Ref(): _referenceCount(1){} Ref::~Ref() {} void Ref::retain() { assert(_referenceCount > 0, "reference count should be greater than 0"); ++_referenceCount; } void Ref::release() { assert(_referenceCount > 0, "reference count should be greater than 0"); --_referenceCount; if(_referenceCount == 0) { delete this } } Ref * Ref::autorelease() { PoolManager::getInstance()->getCurrentPool()->addObject(this); return this; } unsigned int Ref::getReferenceCount() const { return _referenceCount; }
|
可以看到Ref主要就是运用了引用计数,在一个节点的create函数中通常都会都会调用autorelease,在这个函数中调用的是AutoreleasePool的addObject方法。
下面是AutoreleasePool以及PoolManager的源代码实现核心部分摘录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
| class AutoreleasePool { public: AutoreleasePool(); AutoreleasePool(const std::string &name); ~AutoreleasePool(); void addObject(); void clear(); private: std::vector<Ref *> _managedObjectArray; std::string _name; } AutoreleasePool::AutoreleasePool() : _name("") { _managedObjectArray.reserve(150); PoolManager::getInstance()->push(this); } AutoreleasePool::AutoreleasePool(const std::string &name):_name(name) { _managedObjectArray.reserve(150); PoolManager::getInstance()->push(this); } AutoreleasePool::~AutoreleasePool() { clear(); PoolManager::getInstance()->pop(); } void AutoreleasePool::addObject(Ref * object) { _managedObjectArray.push_back(object); } void AutoreleasePool::clear() { std::vector<Ref*> releasings; releasings.swap(_managedObjectArray); for (const auto &obj : releasings) { obj->release(); } }
class PoolManager { public: static PoolManager * getInstance(); AutoreleasePool * getCurrentPool() const; friend class AutoreleasePool; private: PoolManager(); ~PoolManager(); void push(AutoreleasePool * pool); void pop(); static PoolManager * s_singleInstance; std::vector<AutoreleasePool *> _releasePoolStack; } PoolManager * PoolManager::getInstance() { if (s_singleInstance == nullptr) { s_singleInstance = new (std::nothrow) PoolManager(); new AutoreleasePool("cocos2d autorelease pool"); } return s_singleInstance; } PoolManager::PoolManager() { _releasePoolStack.reserve(10); } PoolManager::~PoolManager() { while(!_releasePoolStack.empty()) { AutoreleasePool * pool = _releasePoolStack.back(); delete pool; } } PoolManager::getCurrentPool() const { return _releasePoolStack.back(); } void PoolManager::push(AutoreleasePool * pool) { _releasePoolStack.push_back(pool); } void PoolManager::pop() { assert(!_releasePoolStack.empty()); _releasePoolStack.pop_back(); }
|
总结:
1.autorelease()的实质是将对象加入自动释放池,对象的引用计数不会立刻减1,在自动释放池被回收时对象执行release()。
2.autorelease()只有在自动释放池被释放时才会进行一次释放操作,如果对象释放的次数超过了应有的次数,则这个错误在调用autorelease()时并不会被发现,只有当自动释放池被释放时(通常也就是游戏的每一帧结束时),游戏才会崩溃。在这种情况下,定位错误就变得十分困难了。例如,在游戏中,一个对象含有1个引用计数,但是却被调用了两次autorelease()。在第二次调用autorelease()时,游戏会继续执行这一帧,结束游戏时才会崩溃,很难及时找到出错的地点。因此,我们建议在开发过程中应该避免滥用autorelease(),只在工厂方法等不得不用的情况下使用,尽量以release()来释放对象引用。
3.autorelease()并不是毫无代价的,其背后的释放池机制同样需要占用内存和CPU资源。过多的使用autorelease()会增加自动释放池的管理和释放池维护对象存取释放的支出。在内存和CPU资源本就不足的程序中使得系统资源更加紧张。此时就需要我们合理创建自动释放池管理对象autorelease。
不用的对象推荐使用release()来释放对象引用,立即回收。