Могу ли я установить для свойств значение nil в dealloc при использовании ARC?

125

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

  1. Верно ли, что мне НЕ нужно писать явные операторы свойств выпуска в моем dealloc при использовании ARC? Другими словами, правда ли, что следующее НЕ требует явного освобождения?

    @interface MyClass : NSObject
    @property (strong, nonatomic) NSObject* myProperty;
    @end
    
    @implementation MyClass
    @synthesize myProperty;
    @end
  2. Мой следующий и более важный вопрос исходит из строки в документе Transitioning to ARC Release Notes :

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

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

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

emfurry
источник

Ответы:

197

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

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

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

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

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

Лили Баллард
источник
2
Это имеет смысл! Однако позвольте мне спросить вас: распространенный сценарий, который у меня есть, заключается в том, что у меня есть MyController : UIViewControllerкласс, который создает и владеет UIView, а также устанавливает делегата представления самому себе. Это единственный сохраняющий владелец этой точки зрения. Когда контроллер освобождается, представление также должно быть освобождено. Имеет ли значение, если указатель делегата болтается?
emfurry
4
@emfurry: Вероятно, нет, потому что к тому времени, когда ваш контроллер представления умрет, само представление не должно находиться в иерархии представлений и не должно ничего делать, но лучше не делать предположений. Что, если представление асинхронно запланировало работу, которая должна быть выполнена позже, и само представление в конечном итоге переживет свой контроллер представления на короткое время (например, из-за асинхронной работы, временно сохраняющей представление)? Лучше всего просто обнулить делегата на всякий случай. И на самом деле, если рассматриваемое представление - это UIWebView, в документации явно указано, что вам нужно обнулить делегата.
Лили Баллард,
3
@zeiteisen: Нет. unsafe_unretainedв точности эквивалентен assignсвойству и является нормальным поведением для отношений делегата в MRR, и они должны быть обнулены.
Лили Баллард
4
Я не согласен с утверждением об отказе от использования сеттеров в dealloc с MRC. Apple не рекомендует это делать, но они тоже делают это в своем коде. Фактически вы можете создать новые проблемы, не используя сеттер. По этому поводу ведется несколько больших дискуссий. Что важно, так это правильно написать установщик (он должен вести себя правильно, если вы передадите ему нулевое значение) и иногда следите за порядком освобождения.
Sulthan
7
@Sulthan: Использовать сеттеры в dealloc или нет - это огромная червя, но моя позиция в основном сводится к следующему: вы хотите вызывать как можно меньше кода в dealloc. Сеттеры имеют тенденцию включать побочные эффекты либо путем переопределения в подклассах, либо с помощью KVO, либо других механизмов. Особенно следует избегать побочных эффектов от dealloc, как от чумы. Если вы можете удалить вызов метода из dealloc, сделайте это. Это упрощается до: не вызывать сеттеры в dealloc.
Лили Баллард,
2

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

Краткий ответ : нет, вам не нужно обнулять автосинтезированные свойства в deallocARC. И вам не нужно использовать сеттер для тех, кто в 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, возможно, пришло время еще раз проверить свои сеттеры.

Кер
источник