cocos2d-x学习笔记内存管理之autorelease

2012-12-15 11:45

cocos2d-x学习笔记内存管理之autorelease

       刚开始接触cocos2d-x的时候最让我头疼的就是内存管理,经常写着写着代码心里就开始没谱了,总是在问自己:“这样子会不会有内存泄漏、这个对象能得到释放吗”,类似的情况已经不是一次两次出现了。现在终于对这些问题有了更清晰的认识,就autorelease()做一下总结。

        先从CCObject的autorelease方法入手,代码如下:

1
2
3
4
5
6
7
8
9
CCObject* CCObject::autorelease(void)
{
    //将当前对象添加到内存池中
    CCPoolManager::sharedPoolManager()->addObject(this);
                                             
    //当前对象是否被内存池管理设置为 真
    m_bManaged = true;
    return this;
}

        在这里,当程序执行第4行之前,监视this对象,发现他的应用计数是 1 ,执行完成第4行代码之后应用计数还是 1 。这让我想起很多addObject接口,里面都会对传进去的对象做 retain 操作,这里内存池中难道没有做 retain 操作吗,还是里面retain之后将传进去的对象释放掉了。研究一下内存管理的代码"CCAutoreleasePool.h":

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
#ifndef __AUTORELEASEPOOL_H__
#define __AUTORELEASEPOOL_H__
                                    
#include "CCObject.h"
#include "CCArray.h"
                                    
NS_CC_BEGIN
                                    
/**
 * @addtogroup base_nodes
 * @{
 */
                                    
                                    
 //自动释放池
class CC_DLL CCAutoreleasePool : public CCObject
{
    //保存所有添加到释放池中的对象  
    //注意、、CCArray内部是通过将对象retain 然后存储起来的,应用计数会增加1
    CCArray*    m_pManagedObjectArray;    
public:
    CCAutoreleasePool(void);
    ~CCAutoreleasePool(void);
                                    
    //将对象添加到自动释放池
    void addObject(CCObject *pObject);
                                    
    //将对象从自动释放池中移除
    void removeObject(CCObject *pObject);
                                    
    //将自动释放池中的对象释放掉
    void clear();
};
                                    
                                    
//池管理者、、、、、自动释放池管理者
class CC_DLL CCPoolManager
{
    //用于存放自动释放池的队列
    CCArray*    m_pReleasePoolStack;    
                                    
    //当前的自动释放池,指向自动释放池队列的末尾节点
    CCAutoreleasePool*                    m_pCurReleasePool;
                                    
    //获取当前的自动释放池
    CCAutoreleasePool* getCurReleasePool();
public:
    CCPoolManager();
    ~CCPoolManager();
                                    
    //清空所有的自动释放池
    void finalize();
                                    
    //增加一个自动释放池
    void push();
                                    
    //移除一个自动释放池,即移除当前自动释放池
    void pop();
                                    
    //将对象添加到当前的自动释放池中
    void removeObject(CCObject* pObject);
                                    
    //将对象冲当前的自动释放池中移除
    void addObject(CCObject* pObject);
                                    
    //获取自动释放池管理者、、、整个程序代码中所有需要使用CCPoolManager的地方都通过这个函数获取
    //外部不应该自己new一个内存管理者来使用
    static CCPoolManager* sharedPoolManager();
                                    
    //清理当前内存管理者,释放其中的自动释放池以及自动释放池中的所有对象
    static void purgePoolManager();
                                    
    friend class CCAutoreleasePool;
};
                                    
// end of base_nodes group
/// @}
                                    
NS_CC_END

    现在知道CCObject中的autorelease其实是将自己通过自动释放池管理者 CCPoolManager 添加到其当前的自动释放池 m_pCurReleasePool 中去了,再看看自动释放池管理者以及 自动释放池的实现 "CCAutoreleasePool.cpp":

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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
/****************************************************************************
Copyright (c) 2010 cocos2d-x.org
                     
                     
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
                     
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
                     
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include "CCAutoreleasePool.h"
#include "ccMacros.h"
                     
NS_CC_BEGIN
                     
//静态成员变量,保存整个程序使用的自动释放池 管理者
static CCPoolManager* s_pPoolManager = NULL;
                     
CCAutoreleasePool::CCAutoreleasePool(void)
{
    //初始化队列,用以保存添加到池中的对象
    m_pManagedObjectArray = new CCArray();
    m_pManagedObjectArray->init();
}
                     
CCAutoreleasePool::~CCAutoreleasePool(void)
{
    CC_SAFE_DELETE(m_pManagedObjectArray);
}
                     
//将对象添加到自动释放池中
void CCAutoreleasePool::addObject(CCObject* pObject)
{
    //将对象retain 保存到队列中
    m_pManagedObjectArray->addObject(pObject);
                     
    //到这里,对象的应用计数最少应该是2
    CCAssert(pObject->m_uReference > 1"reference count should be greater than 1");
                     
    //调用对象的release方法,将引用计数减1
    //release方法在CCObject中实现,引用计数减1之后检查引用计数如果为0,则直接删除此对象
    pObject->relearese(); // no ref count, in this case autorelease pool added.
}
                     
void CCAutoreleasePool::removeObject(CCObject* pObject)
{
    //从自动释放池中移除对象,,使该对象不被自动释放池管理,当然也不会自动释放
    //第二个参数为false,,因为addObject被没有使引用计数增加,所有这里也不能使应用计数有变化
    m_pManagedObjectArray->removeObject(pObject, false);
}
                     
//清空自动释放池,释放池内所有对象
void CCAutoreleasePool::clear()
{
    if(m_pManagedObjectArray->count() > 0)
    {
        //CCAutoreleasePool* pReleasePool;
#ifdef _DEBUG
        int nIndex = m_pManagedObjectArray->count() - 1;
#endif
                     
        CCObject* pObj = NULL;
        CCARRAY_FOREACH_REVERSE(m_pManagedObjectArray, pObj)
        {
            if(!pObj)
                break;
                     
            pObj->m_bManaged = false;
            //(*it)->release();
            //delete (*it);
#ifdef _DEBUG
            nIndex--;
#endif
        }
        //移除所有的在队列中的对象,依次调用所有对象的release方法
        m_pManagedObjectArray->removeAllObjects();
    }
}
                     
                     
//--------------------------------------------------------------------
//
// CCPoolManager
//
//--------------------------------------------------------------------
                     
                     
//获取自动释放池管理者
CCPoolManager* CCPoolManager::sharedPoolManager()
{
    if (s_pPoolManager == NULL)
    {
        s_pPoolManager = new CCPoolManager();
    }
    return s_pPoolManager;
}
                     
void CCPoolManager::purgePoolManager()
{
    CC_SAFE_DELETE(s_pPoolManager);
}
                     
CCPoolManager::CCPoolManager()
{
    //初始化自动释放池队列
    m_pReleasePoolStack = new CCArray();    
    m_pReleasePoolStack->init();
                     
    //当前的自动释放池为空
    m_pCurReleasePool = 0;
}
                     
CCPoolManager::~CCPoolManager()
{
    //释放所有的自动释放池
     finalize();
                      
     // we only release the last autorelease pool here 
    m_pCurReleasePool = 0;
     m_pReleasePoolStack->removeObjectAtIndex(0);
                      
     CC_SAFE_DELETE(m_pReleasePoolStack);
}
                     
void CCPoolManager::finalize()
{
    //清空自动释放池队列中的所有自动释放池
    if(m_pReleasePoolStack->count() > 0)
    {
        //CCAutoreleasePool* pReleasePool;
        CCObject* pObj = NULL;
        CCARRAY_FOREACH(m_pReleasePoolStack, pObj)
        {
            if(!pObj)
                break;
            CCAutoreleasePool* pPool = (CCAutoreleasePool*)pObj;
            pPool->clear();
        }
    }
}
                     
void CCPoolManager::push()
{
    //向自动释放池队列中添加一个新的自动释放池,将新添加的自动释放池作为当前的
    //自动释放池使用
    CCAutoreleasePool* pPool = new CCAutoreleasePool();       //ref = 1
    m_pCurReleasePool = pPool;
                     
    m_pReleasePoolStack->addObject(pPool);                   //ref = 2
                     
    pPool->release();                                       //ref = 1
}
                     
void CCPoolManager::pop()
{
    //清理自动释放池队列,只剩下队列中的第一个自动释放池
    //剩下的这个自动释放池中的对象也要清理掉
    //这个函数便是自动释放池管理者,实现自动释放池内对象的实现了
    if (! m_pCurReleasePool)
    {
        return;
    }
                     
     int nCount = m_pReleasePoolStack->count();
                     
     //清理当前的自动释放池
    m_pCurReleasePool->clear();
                      
      if(nCount > 1)
      {
          //如果自动释放池队列中有超过一个自动释放池
          //将末端的自动释放池清理并移除
        m_pReleasePoolStack->removeObjectAtIndex(nCount-1);
        m_pCurReleasePool = (CCAutoreleasePool*)m_pReleasePoolStack->objectAtIndex(nCount - 2);
    }
}
                     
void CCPoolManager::removeObject(CCObject* pObject)
{
    //从当前的自动释放池中移除对象
    CCAssert(m_pCurReleasePool, "current auto release pool should not be null");
                     
    m_pCurReleasePool->removeObject(pObject);
}
                     
void CCPoolManager::addObject(CCObject* pObject)
{
    //将对象添加到当前的自动释放池中
    getCurReleasePool()->addObject(pObject);
}
                     
                     
CCAutoreleasePool* CCPoolManager::getCurReleasePool()
{
                     
    //获取当前的自动释放池
    //如果当前的自动释放池为空,说明自动释放池队列中也为空
    //通过push方法,添加新的自动释放池
    if(!m_pCurReleasePool)
    {
        push();
    }
                     
    CCAssert(m_pCurReleasePool, "current auto release pool should not be null");
                     
    return m_pCurReleasePool;
}
                     
NS_CC_END


        自动释放池管理者通过pop方法,将当前自动释放池中的所有对象调用release方法进行释放,pop方法是什么时候在什么地方进行调用的呢,在导演类CCDirector的实现中有这样的一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//主循环,每一帧都会被调用
void CCDisplayLinkDirector::mainLoop(void)
{
    if (m_bPurgeDirecotorInNextLoop)
    {
        m_bPurgeDirecotorInNextLoop = false;
        purgeDirector();
    }
    else if (! m_bInvalid)
     {
         //绘制场景
         drawScene();
                        
         //释放自动释放池
         CCPoolManager::sharedPoolManager()->pop();        
     }
}

    在每一帧绘制完成之后,当前的自动释放池将会被清理,所有调用了autorelease操作的对象都会被调用release方法,减少其引用计数。如果我们创建的对象调用了autorelease,那么在稍后帧绘制之后,自动释放池被清理的时候此对象的引用计数将被减1,此对象如果没有在其他地方被

retain,那么它将会被释放掉。

    在对象的使用上,为保证对象能被正确的释放,需要时刻知道此对象的引用计数为多少,但是很多时候能做到这点很难,除非对所有的接口都很了解,知道其中是否对当前对象做了retain操作或者release操作,如果做不到这点,可以按照cocos2d-x框架中这样的原则去做

    1、作为参数传进来的对象,如果你要长期的使用或者管理它,请 retain,不用的时候记得release

    2、作为参数传进来的对象,他不是你创建或者retain的,如果你不确定他从哪里来,外面是否知道你会release掉他,请别随便调用release

    3、如果你撇开自动释放池,new了一个对象而不调用autorelease,在不使用的时候直接将对象delete掉,这样的做法是很不安全的,除非你创建的对象真的只有你在使用而没有被其他对象retain。但并非代表你不能自己new和delete管理对象,因时而异。

    4、创建一个新的对象,调用了对象的autorelease方法,如果想长期的使用他,请使用retain方法(包括addChild到自身,addObject到某个

CCArray中),清除时使用release方法(removeChild、CCArray的removeObject等)

posted @ 2013-06-14 10:20  Lunaa  阅读(256)  评论(0编辑  收藏  举报