• cocos2dx学习笔记内存管理之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等)

  • 相关阅读:
    网络安全协议(1)
    CG-CTF(6)
    CG-CTF(5)
    CG-CTF(4)
    CG-CTF(3)
    MAC地址欺骗(原理及实验)
    CG-CTF(2)
    CG-CTF(1)
    【转载】Spring Boot【快速入门】2019.05.19
    【编程大系】Java资源汇总
  • 原文地址:https://www.cnblogs.com/xiayong123/p/3717011.html
Copyright © 2020-2023  润新知