大佬教程收集整理的这篇文章主要介绍了Cocos2d-x 3.1 内存管理机制,大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
Cocos2d-x使用的内存管理方式是引用计数,引用计数是一种很有效的机制,通过给每个对象维护一个引用计数器,记录该对象当前被引用的次数。当对象增加一次引用时,计数器加1;而对象失去一次引用时,计数器减1;当引用计数为0时,标志着该对象的生命周期结束,自动触发对象的回收释放。引用计数的重要规则是每一个程序片段必须负责任地维护引用计数,在需要维持对象生存的程序段的开始和结束分别增加和减少一次引用计数,这样就可以实现十分灵活的内存管理。
接下来看一下Cocos2d-x 3.1 版本的@L_262_2@是怎么实现引用计数的。
一、Ref
我们都知道几乎每个类都继承一个类Ref,打开CCRef.h查看Ref类,去掉其它与引用计数无关的,可以简化为:
1、Ref自己不能实例化,只能由子类实例化;
2、创建是引用计数为1;
3、调用retain引用计数加1;
4、调用release引用计数减1;
5、调用autorelease并没有使引用计数减1,而是交给自动释放池来管理。
那么自动释放池是什么呢?肯定跟autorelease方法里面的PoolManager有关。
打开CCAutoreleasePool.h文件查看,发现有两个类,一个是AutoreleasePool,一个是PoolManager,从字面意思看,AutoreleasePool就是自动释放池,而PoolManager就是池管理器,这些思路有点清晰了:
1、调用autorelease后对象交给AutoreleasePool来管理;
2、PoolManager是用来管理AutoreleasePool的,说明可以有多个池。
二、AutoreleasePool
接下来一步步看,先看AutoreleasePool自动释放池,看简化版本的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
// AutoreleasePool.cpp
AutoreleasePool::AutoreleasePool()
PoolManager::geTinstance()->push(
this
@H_944_500@// 2、新建一个释放池时就加入了释放池管理器中(可以暂时放着,等看了PoolManager再回来看)
}
AutoreleasePool::~AutoreleasePool()
clear();
// 1、清理释放池
}
}
AutoreleasePool::clear()
for
(
const
auto &obj : _managedObjectArray)
// 1、调用所有自动释放对象的release函数,注意:只有当引用计数为0时才会delete对象,相同对象加入几次就会release几次
obj->release();
}
}
|
1、维持一个保存Ref对象的队列,这些Ref对象调用autorelease就会加到该队列,调用addObject函数添加;
2、clear函数对AutoreleasePool管理的所有Ref执行一次release操作,只有当引用计数为0时对象才会delete,加入几次就执行几次release操作。
三、PoolManager
PoolManager是管理释放池的,在AutoreleasePool用到push和pop方法,可以猜到PoolManager应该维持一个存放释放池的栈:
// PoolManager.h
CC_DLL PoolManager
AutoreleasePool *getCurrentPool()
;
// 获取当前的释放池
:
PoolManager();
~PoolManager();
push(AutoreleasePool *pool);
// 压入一个释放池
pop();
// 弹出一个释放池
PoolManager* s_singleInstance;
std::deque _releasePoolStack;
// 存放自动释放池的栈
AutoreleasePool *_curReleasePool;
// 当前的自动释放池
};</autoreleasepool*>
|
// PoolManager.cpp
PoolManager* PoolManager::s_singleInstance = nullptr;
// 获取单例模式时,如果还没创建,则会创建两个释放池并添加到池管理器中
PoolManager* PoolManager::geTinstance()
if
(s_singleInstance == nullptr)
// 第一个池:AutoreleasePool构造函数会将构造的池添加到池管理器中
s_singleInstance->_curReleasePool =
AutoreleasePool();
// 第二个池:将new出来的释放池再一次压入池管理器中
}
return
s_singleInstance;
}
// delete单例模式创建的对象
PoolManager::destroyInstance()
delete s_singleInstance;
s_singleInstance = nullptr;
}
PoolManager::PoolManager()
{
}
// 析构函数
PoolManager::~PoolManager()
while
(!_releasePoolStack.empty())
}
}
// 获取当前释放池
AutoreleasePool* PoolManager::getCurrentPool()
const
_curReleasePool;
}
PoolManager::push(AutoreleasePool *pool)
_curReleasePool = pool;
// 2、设为当前释放池
}
// 弹出栈顶释放池,并将当前释放池指向新的栈顶释放池
PoolManager::pop()
_releasePoolStack.pop_BACk();
(_releasePoolStack.size() >
)
}
貌似PoolManager功能更加简单,就是管理释放池。
2、PoolManager::geTinstance()
在获取单例对象时,如果不存在则会创建一个PoolManager对象,这时候会添加两个释放池 引擎自己会维持两个默认的释放池,如果我们没有手动创建释放池,则autorelease对象都添加到栈顶默认释放池。 其实我还没弄懂这里为什么要有两个默认的释放池,一个也可以的。 3、getCurReleasePool()获取的是当前释放池,addObject()将Ref对象加入当前释放池中。<喎�"http://www.2cto.com/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHA+PC9wPgo8cHJlIGNsYXNzPQ=="brush:java;">void AutoreleasePool::addObject(Ref* object) { _managedObjectArray.push_BACk(object); }这样,每一个调用autorelease的Ref对象都会添加到_managedObjectArray中。 4、自动释放池的对象是怎么释放的?看AutoreleasePool:clear()函数 这里循环_managedObjectArray调用里面对象的release,调用1次release,引用计数就减1,当引用计数为0时就delete该对象。 你肯定很疑惑,在哪里会调用clear函数呢,~AutoreleasePool()会调用,但是那是在delete的时候释放的,我们看到Director类的主循环: 看到这里就明白了吧,每一次主循环都会调用clear来release自动释放池的对象,而每一帧会执行一次主循环,也就是每一帧都会清除一次。 五、手动创建释放池 我们已经知道,调用了autorelease()方法的对象将会在自动释放池池释放的时候被释放一次。虽然Cocos2d-x已经保证每一帧结束后释放一次释放池,但是如果在一帧之内生成了大量的autorelease对象,将会导致释放池性能下降。因此在生存autorelease对象密集的区域(通常是循环中)的前后,最后手动创建一个释放池。
|