Устанавливаю ли я свойства равными нулю в dealloc при использовании ARC?

#objective-c #ios #automatic-ref-counting

#objective-c #iOS #автоматический подсчет ссылок

Вопрос:

Я пытаюсь изучить автоматический подсчет ссылок в iOS 5. Теперь первая часть этого вопроса должна быть простой:

  1. Правильно ли, что мне НЕ нужно писать явные инструкции release-property в моем dealloc при использовании ARC? Другими словами, верно ли, что для следующего НЕ требуется явное освобождение?
     @interface MyClass : NSObject
    @property (strong, nonatomic) NSObject* myProperty;
    @end
    
    @implementation MyClass
    @synthesize myProperty;
    @end
     
  2. Мой следующий и более важный вопрос возникает из строки в документе Переход на ARC Release Notes:

    Вам не нужно (действительно, не может) освобождать переменные экземпляра, но вам может потребоваться вызвать [self setDelegate:nil] для системных классов и другого кода, который не компилируется с использованием ARC.

    Возникает вопрос: как я узнаю, какие системные классы не компилируются с помощью ARC? Когда я должен создавать свой собственный dealloc и явно устанавливать значение nil для строго сохраняемых свойств? Должен ли я предполагать, что все классы NS и UI framework, используемые в properties, требуют явного освобождения?

В SO и других источниках имеется множество информации о методах освобождения резервной копии ivar свойства при использовании отслеживания ссылок вручную, но относительно мало об этом при использовании ARC.

Ответ №1:

Короткий ответ: нет, вам не нужно обнулять свойства в dealloc under ARC.

Длинный ответ: вы никогда не должны обнулять свойства в dealloc , даже при ручном управлении памятью.

В MRR вы должны освободить свои ivar. Обнуление свойств означает вызов установщиков, которые могут вызывать код, который он не должен затрагивать dealloc (например, если ваш класс или подкласс переопределяет установщик). Аналогично это может вызвать уведомления KVO. Вместо этого освобождение ivar позволяет избежать этих нежелательных действий.

В ARC система автоматически освобождает любые ivar для вас, поэтому, если это все, что вы делаете, вам даже не нужно внедрять dealloc . Однако, если у вас есть какие-либо не объектные ивары, которые требуют специальной обработки (например, выделенные буферы, которые вам нужны free() ), вам все равно придется иметь дело с ними dealloc .

Кроме того, если вы установили себя в качестве делегата каких-либо объектов, вам следует отменить это отношение в dealloc (это немного о вызове [obj setDelegate:nil] ). Примечание о выполнении этого для классов, которые не компилируются с помощью ARC, является намеком на слабые свойства. Если класс явно помечает свое delegate свойство как weak , то вам не нужно этого делать, потому что природа слабых свойств означает, что оно будет заполнено для вас. Однако, если свойство помечено assign , вы должны обнулить его в своем dealloc , иначе класс останется с висячим указателем и, скорее всего, произойдет сбой, если он попытается отправить сообщение своему делегату. Обратите внимание, что это относится только к не сохраненным связям, таким как делегаты.

Комментарии:

1. Это имеет смысл! Позвольте мне спросить вас об этом: распространенный сценарий, который у меня есть, заключается в том, что у меня есть MyController : UIViewController класс, который создает и владеет UIView, а также устанавливает делегат представления для себя. Это единственный сохраняющий владелец этого представления. Когда контроллер освобождается, представление также должно быть освобождено. Имеет ли значение, если указатель делегата висит?

2. @emfurry: вероятно, это не так, потому что к моменту смерти вашего контроллера представления само представление не должно находиться в иерархии представлений и ничего не должно делать, но лучше не делать предположений. Что, если представление асинхронно запланировало выполнение работы позже, а само представление в конечном итоге на короткое время переживает свой контроллер представления (например, из-за того, что асинхронная работа временно сохраняет представление)? Лучше всего просто отключить делегат, чтобы быть в безопасности. И на самом деле, если рассматриваемое представление является a UIWebView , в документах явно указано, что вам нужно обнулить делегат.

3. @zeiteisen: Нет. unsafe_unretained точно эквивалентно assign свойству и является нормальным поведением для отношений делегирования в MRR, и их необходимо исключить.

4. Я не согласен с утверждением о том, что сеттеры не используются в dealloc с MRC. Apple не рекомендует этого делать, но они тоже делают это в своем коде. На самом деле вы можете создать новые проблемы, не используя установщик. Есть несколько больших дискуссий по этому поводу. Важно правильно записать установщик (он должен вести себя правильно, если вы передаете ему нулевое значение) и иногда следить за порядком освобождения.

5. @Sulthan: Использовать или не использовать сеттеры в dealloc — это огромная проблема, но моя позиция в основном сводится к следующему: вы хотите вызывать как можно меньше кода в dealloc. Сеттеры имеют тенденцию включать побочные эффекты, либо путем переопределения в подклассах, либо с помощью KVO, либо других механизмов. Особенно следует избегать побочных эффектов при использовании dealloc, таких как чума. Если вы можете удалить вызов метода из dealloc, вы должны это сделать. Это упрощается до: не вызывать установщики в dealloc.

Ответ №2:

Просто чтобы дать противоположный ответ…

Короткий ответ: нет, вам не нужно обнулять автоматически синтезируемые свойства в dealloc under ARC. И вам не нужно использовать сеттер для тех, кто входит init .

Длинный ответ: вы должны обнулять свойства, синтезированные dealloc пользователем, даже в ARC. И вы должны использовать сеттер для тех , кто входит init .

Дело в том, что ваши специально синтезированные свойства должны быть безопасными и симметричными относительно аннулирования.

Возможный установщик для таймера:

 -(void)setTimer:(NSTimer *)timer
{
    if (timer == _timer)
        return;

    [timer retain];
    [_timer invalidate];
    [_timer release];
    _timer = timer;
    [_timer fire];
}
 

Возможный установщик для scrollview, tableview, webview, textfield, …:

 -(void)setScrollView:(UIScrollView *)scrollView
{
    if (scrollView == _scrollView)
        return;

    [scrollView retain];
    [_scrollView setDelegate:nil];
    [_scrollView release];
    _scrollView = scrollView;
    [_scrollView setDelegate:self];
}
 

Возможный установщик для свойства KVO:

 -(void)setButton:(UIButton *)button
{
    if (button == _button)
        return;

    [button retain];
    [_button removeObserver:self forKeyPath:@"tintColor"];
    [_button release];
    _button = button;
    [_button addObserver:self forKeyPath:@"tintColor" options:(NSKeyValueObservingOptions)0 context:NULL];
}
 

Тогда вам не нужно будет дублировать какой-либо код для dealloc , didReceiveMemoryWarning , viewDidUnload , … и ваша собственность может быть безопасно обнародована. Если вы беспокоились о нулевых свойствах dealloc , то, возможно, пришло время еще раз проверить свои сеттеры.