Понимание подсчета ссылок с Какао и Objective-C - PullRequest
121 голосов
/ 09 августа 2008

Я только начинаю смотреть на Objective-C и Cocoa, чтобы поиграть с iPhone SDK. Я достаточно комфортно отношусь к концепции C malloc и free, но схема подсчета ссылок в Cocoa меня довольно смущает. Мне сказали, что это очень элегантно, когда ты это понимаешь, но я еще не перебрал горб.

Как работают release, retain и autorelease и каковы соглашения об их использовании?

(Или, если не получилось, что вы прочитали, что помогло вам получить это?)

Ответы [ 14 ]

148 голосов
/ 09 августа 2008

Давайте начнем с retain и release; autorelease - это действительно особый случай, когда вы понимаете основные понятия.

В Какао каждый объект отслеживает, сколько раз на него ссылаются (в частности, базовый класс NSObject реализует это). Вызывая retain для объекта, вы говорите ему, что хотите увеличить его счетчик ссылок на единицу. Вызывая release, вы сообщаете объекту, что отпускаете его, и его счетчик ссылок уменьшается. Если после вызова release счетчик ссылок теперь равен нулю, то память этого объекта освобождается системой.

Основной способ, которым это отличается от malloc и free, заключается в том, что любому данному объекту не нужно беспокоиться о сбоях других частей системы, поскольку вы освободили память, которую они использовали. Предполагая, что все играют и сохраняют / освобождают в соответствии с правилами, когда один фрагмент кода сохраняет и затем освобождает объект, любой другой фрагмент кода, также ссылающийся на объект, не будет затронут.

Что иногда может сбить с толку, так это знание обстоятельств, при которых вам следует звонить retain и release. Мое эмпирическое правило заключается в том, что если я хочу удерживать объект в течение некоторого промежутка времени (например, если это переменная-член в классе), то мне нужно убедиться, что счетчик ссылок объекта знает обо мне. Как описано выше, счетчик ссылок на объект увеличивается путем вызова retain. По соглашению, он также увеличивается (на самом деле устанавливается на 1), когда объект создается методом "init". В любом из этих случаев я отвечаю за вызов release на объекте, когда я закончу с ним. В противном случае произойдет утечка памяти.

Пример создания объекта:

NSString* s = [[NSString alloc] init];  // Ref count is 1
[s retain];                             // Ref count is 2 - silly
                                        //   to do this after init
[s release];                            // Ref count is back to 1
[s release];                            // Ref count is 0, object is freed

Теперь для autorelease. Авторелиз используется в качестве удобного (и иногда необходимого) способа сообщить системе, что через некоторое время освободить этот объект. С точки зрения сантехники, когда вызывается autorelease, текущий поток NSAutoreleasePool получает предупреждение о вызове. NSAutoreleasePool теперь знает, что как только он получает возможность (после текущей итерации цикла событий), он может вызвать release для объекта. С нашей точки зрения, как программистов, он заботится о том, чтобы позвонить нам release, поэтому нам не нужно (и на самом деле мы не должны).

Важно отметить, что (опять же, по соглашению) все методы создания объектов class возвращают объект с автоматическим освобождением. Например, в следующем примере переменная "s" имеет счетчик ссылок, равный 1, но после завершения цикла событий он будет уничтожен.

NSString* s = [NSString stringWithString:@"Hello World"];

Если вы хотите повесить эту строку, вам нужно явно вызвать retain, а затем явно release, когда вы закончите.

Рассмотрим следующий (очень надуманный) фрагмент кода, и вы увидите ситуацию, когда требуется autorelease:

- (NSString*)createHelloWorldString
{
    NSString* s = [[NSString alloc] initWithString:@"Hello World"];

    // Now what?  We want to return s, but we've upped its reference count.
    // The caller shouldn't be responsible for releasing it, since we're the
    // ones that created it.  If we call release, however, the reference 
    // count will hit zero and bad memory will be returned to the caller.  
    // The answer is to call autorelease before returning the string.  By 
    // explicitly calling autorelease, we pass the responsibility for
    // releasing the string on to the thread's NSAutoreleasePool, which will
    // happen at some later time.  The consequence is that the returned string 
    // will still be valid for the caller of this function.
    return [s autorelease];
}

Я понимаю, что все это немного сбивает с толку - в какой-то момент, однако, оно щелкнет. Вот несколько ссылок, чтобы вы начали:

  • Введение Apple в управление памятью.
  • Программирование какао для Mac OS X (4-е издание) , Аарон Хиллегас - очень хорошо написанная книга с множеством замечательных примеров. Это читается как учебник.
  • Если вы действительно погружаетесь, вы можете отправиться на Ранчо Большого Ботаника . Это учебный центр, которым руководит Аарон Хиллегас - автор упомянутой выше книги. Я посещал курс «Введение в какао» там несколько лет назад, и это был отличный способ учиться.
10 голосов
/ 11 августа 2008

Если вы понимаете процесс сохранения / освобождения, то есть два золотых правила, которые являются «очевидными» для опытных программистов Какао, но, к сожалению, они редко четко излагаются для новичков.

  1. Если функция, которая возвращает объект, имеет alloc, create или copy в своем имени, тогда объект принадлежит вам. Вы должны позвонить [object release], когда закончите с этим. Или CFRelease(object), если это объект Core-Foundation.

  2. Если в названии НЕ указано одно из этих слов, то объект принадлежит кому-то другому. Вы должны вызвать [object retain], если хотите сохранить объект после окончания своей функции.

Было бы неплохо также следовать этому соглашению в функциях, которые вы создаете сами.

(Nitpickers: Да, к сожалению, есть несколько вызовов API, которые являются исключениями из этих правил, но они редки).

8 голосов
/ 10 августа 2008

Если вы пишете код для рабочего стола и можете ориентироваться на Mac OS X 10.5, вам следует хотя бы изучить использование сборки мусора в Objective-C. Это действительно упростит большую часть вашей разработки - вот почему Apple прилагает все усилия для того, чтобы создать ее и обеспечить ее высокую производительность.

Что касается правил управления памятью, когда GC не используется:

  • Если вы создаете новый объект, используя +alloc/+allocWithZone:, +new, -copy или -mutableCopy или если вы -retain объект, вы вступаете во владение им и должны убедиться, что он отправлен -release.
  • Если вы получаете объект любым другим способом, вы не являетесь его владельцем и должны не гарантировать, что он отправлен -release.
  • Если вы хотите убедиться, что объект отправлен -release, вы можете либо отправить его самостоятельно, либо вы можете отправить объект -autorelease, и текущий пул автоматического выпуска отправит его -release ( один раз за получение -autorelease), когда бассейн сливается.

Обычно -autorelease используется как способ обеспечения того, чтобы объекты жили в течение продолжительности текущего события, но впоследствии очищались, поскольку существует пул автоматического выпуска, который окружает обработку событий Какао. В Какао намного более распространено возвращать вызывающему объекту объекты, которые автоматически высвобождаются, чем возвращать объекты, которые сам вызывающий должен освободить.

6 голосов
/ 19 декабря 2008

Я не буду вдаваться в подробности сохранения / выпуска, за исключением того, что вы можете подумать о том, чтобы сбросить 50 долларов и получить книгу Hillegass, но я настоятельно рекомендую начать использовать инструменты Instruments на самых ранних этапах разработки вашего приложения (даже твой первый!). Для этого запустите-> Начать с инструментов повышения производительности. Я бы начал с Leaks, который является лишь одним из многих доступных инструментов, но поможет показать вам, когда вы забыли выпустить. Это просто пугает, сколько информации вы будете представлены. Но посмотрите этот урок, чтобы быстро начать работу:
РУКОВОДСТВО ПО КАКАО: УСТАНОВКА УТЕЧКИ ПАМЯТИ С ИНСТРУМЕНТАМИ

На самом деле попытка форсировать утечек может быть лучшим способом, в свою очередь, научиться предотвращать их! Удачи;)

6 голосов
/ 20 октября 2008

Как всегда, когда люди начинают пытаться перефразировать справочный материал, они почти всегда получают что-то неправильно или дают неполное описание.

Apple предоставляет полное описание системы управления памятью Какао в Руководство по программированию управления памятью для какао , в конце которого приводится краткое, но точное изложение Правил управления памятью .

6 голосов
/ 09 августа 2008

Джошуа (# 6591) - Сборка мусора в Mac OS X 10.5 выглядит довольно круто, но недоступна для iPhone (или если вы хотите, чтобы ваше приложение работало на версиях Mac OS X до 10.5).

Кроме того, если вы пишете библиотеку или что-то, что может быть повторно использовано, использование режима GC блокирует любого, кто использует код, также использовать режим GC, так что, насколько я понимаю, любой, кто пытается написать код многократного использования, имеет тенденцию перейти к управлению памятью вручную.

6 голосов
/ 09 августа 2008

Objective-C использует Подсчет ссылок , что означает, что у каждого Объекта есть счетчик ссылок. Когда объект создан, он имеет счетчик ссылок «1». Проще говоря, когда объект упоминается (то есть хранится где-то), он «сохраняется», что означает, что его счетчик ссылок увеличивается на единицу. Когда объект больше не нужен, он «освобождается», что означает, что его счетчик ссылок уменьшается на единицу.

Когда счетчик ссылок на объект равен 0, объект освобождается. Это основной подсчет ссылок.

Для некоторых языков ссылки автоматически увеличиваются и уменьшаются, но target-c не является одним из этих языков. Таким образом, программист несет ответственность за сохранение и освобождение.

Типичный способ написания метода:

id myVar = [someObject someMessage];
.... do something ....;
[myVar release];
return someValue;

Проблема необходимости не забывать высвобождать любые полученные ресурсы внутри кода является утомительной и подверженной ошибкам. Objective-C представляет другую концепцию, нацеленную на то, чтобы сделать это намного проще: пулы Autorelease. Пулы автоматического освобождения - это специальные объекты, которые устанавливаются в каждом потоке. Это довольно простой класс, если вы посмотрите NSAutoreleasePool.

Когда объект получает сообщение «autorelease», отправленное ему, объект будет искать любые пулы автоматического выпуска, находящиеся в стеке для этого текущего потока. Он добавит объект в список как объект для отправки сообщения «release» в какой-то момент в будущем, как правило, когда освобождается сам пул.

Взяв код выше, вы можете переписать его, чтобы он был короче и проще для чтения, сказав:

id myVar = [[someObject someMessage] autorelease];
... do something ...;
return someValue;

Поскольку объект автоматически освобожден, нам больше не нужно явно вызывать «release» для него. Это потому, что мы знаем, что какой-то пул авторелизов сделает это за нас позже.

Надеюсь, это поможет. Статья в Википедии хороша для подсчета ссылок. Более подробную информацию о пулах автоматического выпуска можно найти здесь . Также обратите внимание, что если вы собираете для Mac OS X 10.5 и более поздних версий, вы можете указать Xcode собирать с включенной сборкой мусора, что позволяет полностью игнорировать retain / release / autorelease.

5 голосов
/ 10 января 2009

Моя обычная коллекция статей по управлению памятью Какао:

управление памятью какао

5 голосов
/ 09 августа 2008

Мэтт Диллард написал :

return [[s autorelease] release];

Авто-релиз не сохраняет объект. Авто-релиз просто помещает его в очередь, которая будет выпущена позже. Вы не хотите, чтобы там было заявление о выпуске.

4 голосов
/ 08 декабря 2008

В сети iDeveloperTV Network есть бесплатная скринкаст

Управление памятью в Objective-C

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...