Подтвердить что ты не робот

Понимание подсчета ссылок с помощью Cocoa и Objective-C

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

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

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

4b9b3361

Ответ 1

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

В Cocoa каждый объект отслеживает, сколько раз он ссылается (в частности, базовый класс 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 для нас, поэтому нам не нужно (и на самом деле мы не должны).

Важно отметить, что (опять же, по соглашению) все методы класса создания объектов возвращают автореализованный объект. Например, в следующем примере переменная "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 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 в управление памятью.
  • Cocoa Программирование для Mac OS X (4-е издание) от Aaron Hillegas - очень хорошо написанная книга с множеством замечательных примеров. Он читается как учебник.
  • Если вы действительно погружаетесь, вы можете отправиться в Big Nerd Ranch. Это учебное заведение, которое ведет Аарон Хильгамас - автор книги, упомянутой выше. Я посещал Intro до курса Cocoa там несколько лет назад, и это был отличный способ узнать.

Ответ 2

Если вы понимаете процесс сохранения/освобождения, тогда есть два золотых правила, которые "duh" очевидны для установленных программистов Cocoa, но, к сожалению, они редко излагаются это явно для новичков.

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

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

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

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

Ответ 3

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

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

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

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

Ответ 4

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

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

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

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

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

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

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

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

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

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

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

Ответ 5

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

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

Ответ 6

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

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

Ответ 7

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

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

Ответ 8

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

return [[s autorelease] release];

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

Ответ 9

Ответ NilObject - хорошее начало. Здесь есть дополнительная информация, относящаяся к ручному управлению памятью (требуется на iPhone).

Если вы лично alloc/init объект, он содержит счетчик ссылок 1. Вы отвечаете за очистку после него, когда он больше не нужен, либо путем вызова [foo release], либо [foo autorelease]. релиз очищает его сразу, в то время как autorelease добавляет объект в пул авторесурсов, который автоматически освободит его позже.

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

Если вы приобрели объект, где вы не вызывали alloc/init, чтобы получить его - например:

foo = [NSString stringWithString:@"hello"];

но вы хотите привязываться к этому объекту, вам нужно вызвать [foo сохранить]. В противном случае, возможно, он получит autoreleased, и вы будете держаться за нулевую ссылку (как в приведенном выше примере stringWithString). Когда вам это больше не нужно, вызовите [foo release].

Ответ 12

В приведенных выше ответах даются четкие подтверждения того, что говорится в документации; проблема, с которой сталкиваются самые новые люди, - это недокументированные случаи. Например:

  • Autorelease: docs говорят, что в какой-то момент в будущем он будет запускать выпуск. " КОГДА?! В принципе, вы можете рассчитывать на объект, находящийся вокруг, пока вы не вернете свой код обратно в цикл системных событий. Система МОЖЕТ освободить объект в любое время после текущего цикла событий. (Я думаю, что Мэтт сказал это раньше).

  • Статические строки: NSString *foo = @"bar"; - вам нужно сохранить или отпустить это? Нет. Как насчет

    -(void)getBar {
        return @"bar";
    }
    

    ...

    NSString *foo = [self getBar]; // still no need to retain or release
    
  • Правило создания. Если вы создали его, вы его обладаете и, как ожидается, его выпустите.

В общем, способ, которым переполняются новые программисты Cocoa, - это не понимание того, какие подпрограммы возвращают объект с помощью retainCount > 0.

Вот фрагмент из Очень простые правила для управления памятью В Cocoa:

Правила счета удержания

  • В рамках данного блока использование -copy, -alloc и -retain должно равняться использованию -release и -autorelease.
  • Объекты, созданные с использованием конструкторов удобства (например, NSString stringWithString), считаются автореализованными.
  • Внедрить метод -dealloc для освобождения экземпляров, которые вы используете

1-я марка говорит: если вы вызвали alloc (или new fooCopy), вам нужно вызвать release на этом объекте.

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

Третий должен быть понятным.

Ответ 14

Как уже упоминалось уже несколько человек, Apple Intro to Memory Management - безусловно лучшее место для начала.

Одна полезная ссылка, которую я еще не видел, - Практическое управление памятью. Вы найдете его в середине документов Apple, если прочитаете их, но это стоит прямой ссылки. Это блестящее резюме правил управления памятью с примерами и распространенными ошибками (в основном, какие другие ответы здесь пытаются объяснить, но не так).