Как я могу безопасно переключаться между методами ARC и MRC без необходимости сохранять / освобождать вызовы?

У меня есть класс ARC со следующим кодом:

[object doStuffWithObject:otherObject];

object"s -doStuffWithObject: Метод скомпилирован с ARC, и это:

- (void)doStuffWithObject:(id)otherObject
{
    DoStuffHelper(object, otherObject);
}

DoStuffHelper, функция C, не скомпилирована с ARC (по соображениям производительности). В DoStuffHelperмне нужно позвонить -retain в начале для object а также otherObject а также -release для них в конце?

4 ответа

Решение

См. Раздел " Избегайте выделения выделенных объектов, которые вы используете", в разделе " Расширенное управление памятью: практическое управление памятью", в котором они отмечают, что "полученные объекты обычно должны оставаться действительными во всей области вызывающего метода", т.е. retain а также release не являются необходимыми, за исключением следующего предостережения:

Есть редкие исключения из этого правила, в первую очередь попадающие в одну из двух категорий.

  1. Когда объект удаляется из одного из базовых классов коллекции.

    heisenObject = [array objectAtIndex:n];
    [array removeObjectAtIndex:n];
    // heisenObject could now be invalid.
    

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

  2. Когда "родительский объект" освобожден.

    id parent = <#create a parent object#>;
     // ...
     heisenObject = [parent child] ;
     [parent release]; // Or, for example: self.parent = nil;
     // heisenObject could now be invalid.
    

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

Чтобы защитить от этих ситуаций, вы сохраняете heisenObject после получения, и вы отпускаете его, когда закончите с ним. Например:

heisenObject = [[array objectAtIndex:n] retain];
[array removeObjectAtIndex:n];
// Use heisenObject...
[heisenObject release];

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

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

Кроме того, если бы ваша служебная функция создавала и возвращала объект, то это было бы другое дело (например, вы обычно autorelease объекты, которые вы возвращали).

Нет. DoStuffHelper не является объектом, который утверждает право собственности на object или же otherObject, Это просто служебная функция, которая работает с ними напрямую. По сути, это часть вашего класса ARC, который уже выполняет полное управление памятью для этих объектов.

В подавляющем большинстве случаев вы не найдете ARC медленнее, чем традиционные сохранения / выпуска. Вы использовали инструменты, чтобы проверить проблему?

Сказав это, вам не нужно сохранять / освобождать объект в DoStuffHelper(), потому что retainCount уже будет>= 1 при входе. Если объекты хранятся в статических или глобальных объектах, вам необходимо их сохранить.

DoStuffHelper, функция C, не скомпилирована с ARC (по соображениям производительности).

Есть ли у вас измерения производительности, которые показывают, что ARC медленнее?

В общем случае исполняемые файлы ARC, сгенерированные с включенным оптимизатором, будут быстрее, чем тот же код с отключенным ARC (и оптимизирован компилятором).

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

Если у вас есть шаблон кода, где это не так, я бы хотел зафиксировать этот шаблон и сообщить об ошибке.

(Это, по общему признанию, мета-ответ. Он ставит под сомнение необходимость даже в таком интерфейсе ARC-MRR. Поддерживая кодовые базы, которые являются частично ARC и частично MRR, такие смеси изобилуют хрупкостью.)

Другие вопросы по тегам