引用计数:每一个对象都维护了一个 对象所有引用的计数值(表示有多少这个对象的引用,在堆内存中,只有一份该对象资源,但是由堆是共享的,那么该对象资源可以在其他函数内通过对象指针引用)。当一个新的引用指向对象时,引用计数器就递增,当去掉一个引用时,引用计数就递减。当引用计数到零时,该对象就将释放占有的资源。
举例说明
如上图所示,我们在fun()函数中,通过Sprite::create(“xxx.png”)方式实例化一个Sprite对象,然后返回对象指针,由于采用create方式生成的过程中,会主动调用autorelease(),将sp指向的对象交由自动释放池(AutoreleasePool)管理,而默认的自动释放池也是一个堆上对象(new出来的),所以一般通过create方式创建的对象,实际只会在程序结束时才释放内存。那么既然create创建出来的对象会一直在堆内存之中,而堆是线程间共享的,所以只要能够获得对象指针,那么就可以在线程间访问该对象,自然不同函数之间也可以访问该对象。
源码分析
以下是摘自3.9版本的源码:
class Ref
{
public:
Ref();
~Ref();
void retain();
void release();
Ref* autorelease();
protected:
friend class AutoreleasePool;
private:
unsigned int m_referenceCount;
}
Ref::Ref()
{
m_referenceCount = 1;//一个新的Ref对象,引用计数将初始化为1
}
Ref::~Ref()
{ //虽然create方式中,也是通过new创建的堆上对象,但是由于autorelease了,所以通过create创建的对象,不要自己主动调用delete来释放内存,如果想释放内存,可以调用release()。
}
void Ref::retain()
{
++m_referenceCount;
}
void Ref::release()
{//release是能够做到安全的管理内存,只有当对象没有被引用时,才delete该对象。
--m_referenceCount;
if(m_referenceCount == 0)
{
delete this;
}
}
void Ref::autorelease()
{
PoolManager::getInstance()->getCurrentPool()->addObject(this);
return this;
}
分析
Ref对象实例化的时候,引用计数将初始化为1,而析构函数中,并未将引用计数-1;为此,为了保证能正确管理内存。对于new 出来的 Ref 对象,如果调用了autorelease(),那么即使之前是new出来的,也不应该通过delete释放,因为autorelease()将本对象交由AutoreleasePool自动管理。
Autorelease实际上只是把对release的调用延迟了,对于每一个autorelease,系统只是把该对象放入了当前的AutoreleasePool中,当该pool被释放时,该pool中的所有对象会被调用release。而默认的pool对象(默认只有一个pool)也是堆上对象。
为了优化内存使用,当某个Ref对象只需要使用一次,那么就在该函数中实例化一个局部变量AutoreleasePool pool,那么当本函数结束时,根据局部变量的生命周期,pool将在函数结束后释放,并主动调用池中对象的release(),从而释放对象的内存。