ПРОГРАММИРОВАНИЕ ПОД IPHONE, IPAD OBJECTIVE-C часть 3

Управление памятью в Cocos2D

На данный момент, нам нужно немного поговорить об управлении памятью и autorelease сообщениях. Управление памятью и подсчетом ссылок в мире Objective-C управляют два простых правила:

 

  • Если у вас есть (alloc, copy, или retain) объекта, вы должны освободить его позже или сделать autorelease.
  • Если вы посылаете объекту autorelease, вы не должны освобождать его.
Обычно, когда Вы создаете объект в Objective-C, то делаете это с помощью вызова alloc и становитесь ответственным за освобождение объекта, когда он больше не нужен. Вот типичное использование alloc/init и release:
<code data-result="[object Object]">// создание нового объекта NSObject 
NSObject* myObject = [[NSObject alloc] init]; 
// делаем что-то с myObject
// освобождаем память после myObject 
// елси не освободили - создали утечку  
// и эта часть памяти никогдане не освободиться. 
[myObject release];</code>
Давайте  напишем код с использование autorelease сообщения. Теперь вы можете избежать отправки сообщений release . Вот тот же пример, переписанный с использованием autorelease:
<code data-result="[object Object]">// создание нового объекта NSObject 
NSObject* myObject = [[[NSObject alloc] init] autorelease]; 
// делаем что-то с myObject
// не нужно посылать release, в противном случае - приложение упадет</code>
Как  видите, это упрощает в некоторой степени управление памятью и вам больше не потребуется запоминать, что нужно отправить сообщение release. Autorelease pool заботится о том, чтобы за вас отправить объекту сообщение release в конце текущего обновления кадра, если он больше не используется вами. Создание объекта становится чуть более сложным, потому что было добавленоautorelease-сообщение.
Теперь рассмотрим следующий код, который показывает, как бы вы создавали CCNode объект, если бы следовали традиционному стилю:
<code data-result="[object Object]">//создаем новый объект типа CCNode 
CCNode* myNode = [[CCNode alloc] init]; 
//делаем что-то с myNode… 
[myNode release];</code>
Это не является предпочтительным способом создания объектов в cocos2d. Гораздо проще использовать статические методы инициализации, которые возвращают autorelease объекты. Вопреки тому, что Apple рекомендует постоянное использование autoreleaseвстроенных в конструкцию cocos2d, перемещая вызовы, такие как [[[NSObject alloc] init] autorelease] для статического метода в самом классе. Это сделает вашу жизнь проще, так как Вы не должны помнить, какие объекты должны быть освобождены, которые часто вызывают либо сбой (из-за чрезмерного создания определенных объектов) или утечки памяти (из-за не удаления всех объектов которые вы создавали).
В случае класса CCNode статический инициализатор + (id) node. Следующий код отправляет alloc сообщения для self, что эквивалентно использованию [CCNode alloc], если код находится в CCNode реализации.
<code data-result="[object Object]">+ (id)node  
{ 
    return [[[self alloc] init] autorelease]; 
}</code>
Видя это, мы можем переписать CCNode, используя статический инициализатор, и совсем неудивительно, что код становится краток, лаконичен и аккуратен:
<code data-result="[object Object]">//создаем новый объект типа CCNode 
CCNode* myNode = [CCNode node]; 
//делаем что-то с myNode…</code>
В этом прелесть использования autorelease-объектов. Вам не нужно помнить об отправке им release-сообщения. Но есть один нюанс. Если вы используете этот код и позже захотите получить доступ к myNode-объекту, который уже удален, то это повлечет за собой вызовет EXC_BAD_ACCESS и произойдет аварийных выход с приложения, другими словами crash.
Простое добавление переменной CCNode* myNode в класс не означает, что для объекта будет автоматически выделена память. Если вы хотите в дальнейших функциях использовать autorelease объект — вам следует послать ему команду retain, а по окончанию использования — release.
Но есть еще лучший способ использования autorelease-объектов, удерживая их без использования метода retain. Обычно вы создаете CCNode-объекты и добавляете их на scene в иерархию Node-объектов, как дочерних. Более того, вы можете избавиться от самой переменно myNode, используя методы cocos2d для хранения объектов.
<code data-result="[object Object]">//создаем autorelease объект типа CCNode 
-(void) init 
{ 
    myNode = [CCNode node]; 
    myNode.tag = 123; 
    //добавляем myNode как дочерний на screen
    [self addChild:myNode]; 
} 
-(void)update:(ccTime)delta 
{ 
    //позже получаем объект и используем myNode снова
    CCNode* myNode = [self getChildByTag:123]; 
    //делаем что-то с myNode 
}</code>
Магия в том, что addChild добавляет объект в коллекцию. В этом случае CCArray похоже на NSMutableArray  в iOS SDK, но быстрее.CCArray , NSMutableArray и любая другая IOS SDK- коллекция, автоматически отправляет retain-сообщение для любого добавленного объекта, а также отправляет  release-сообщение  для любого объекта, что был  удален из коллекции. Таким образом, объект остается в памяти и доступен в дальнейшем, пока не будет удален из коллекции и автоматически освобожден.
Помните, что при управлении памятью для объектов в cocos2d лучше всего применять способы, описанные в этом уроке. Вы можете столкнуться с другими разработчиками, которые говорят, что использование autorelease это плохо или медленно и autorelease лучше не использовать. Не слушайте их.
ПРИМЕЧАНИЕ: В документации Apple, разработчики рекомендует сократить число autorelease -объектов. Большинство cocos2d-объекты создаются как autorelease объекты. Это делает управление памятью гораздо проще, как я уже показал.
Если вы начнете использовать alloc/init и release для каждого cocos2d-объекта, то получите для себя много головной боли. Это не означает, что вы никогда не будете использовать alloc/init, вы будете его использовать, иногда это даже необходимо. Но для cocos2d-объектов вы должны полагаться на использование статического autorelease инициализатора.
В Autorelease-объектов есть только один нюанс — их память используется пока игра продвигается на один кадр. Это означает, что если вы создаете много одноразовых autorelease-объектов в каждом кадре, вы можете неэффективно тратить память. Но это действительно редкое явление.
И самое главное. Все мы привыкли работать с ARC. К хорошему привыкаешь быстро и быстро забываешь о старых неудачах при работе с памятью. Так вот, Cocos2D официально не поддерживает ARC, по крайней мере, его нету в v1.0.1 и 2.0 beta тоже.
На этом заканчивается тема управления памятью cocos2d. Для более углубленного изучения  управления памятью устройства можете обратиться к Advanced Memory Management Programming Guide.

Comments are closed.