Должны ли вы установить делегат на ноль в классе, используя делегат или в самом классе

Если класс A использует класс B, а класс A является делегатом класса B, нормально ли, если в делелоке класса B для делегата установлено значение nil? Я видел код, обычно сбрасывающий делегата на nil внутри Dealloc класса A, но не был уверен, что реальная разница делает это так или иначе.

например Это обычный способ:

// somewhere in class A

- (void) someFunc {
  self.b = [[B alloc] init];
  self.b.delegate = self;
}

- (void) dealloc {
  self.b.delegate = nil;
  [self.b release];
}

person Boon    schedule 01.07.2009    source источник
comment
self.b = [[B alloc] init]; вы никогда не должны использовать selfна левой стороне, если вы используете alloc на правой стороне, если @property для b сохраняется. Просто хотел добавить.   -  person thesummersign    schedule 22.03.2012
comment
@geekay, почему это?   -  person Brad Thomas    schedule 21.10.2014
comment
это было во время без дуги   -  person thesummersign    schedule 10.02.2015


Ответы (3)


Да, вы должны установить для свойства делегата класса B значение nil в Dealloc класса A.

Это не проблема управления памятью, потому что свойства делегата должны быть помечены как назначенные, а не сохраненные, чтобы избежать циклов сохранения (иначе Dealloc никогда не будет вызываться). Проблема в том, что в противном случае класс B мог бы отправить сообщение классу A после того, как он был выпущен.

Например, если у classB есть вызов delagate, чтобы сказать, что он скрыт, и classB освобождается сразу после classA, он будет сообщать уже освобожденному classA, вызывая сбой.

И помните, вы не всегда можете гарантировать выполнение заказа, особенно если они автоматически освобождаются.

Так что да, обнулите свойство делегата в делелоке classA.

person Peter N Lewis    schedule 02.07.2009
comment
+1 Хороший момент о том, что B, возможно, обменивается сообщениями с A. Скорее всего, это в многопоточном сценарии, но никогда не знаешь наверняка. Это отличное приложение для __weak ссылок, если вы используете GC. Я думаю, делегаты всегда должны быть __weak в новом коде. - person Quinn Taylor; 02.07.2009

Насколько я знаю, лучше всего (назначать) делегата, чтобы вы избегали циклических ссылок на счетчики сохранения для подобных ситуаций. Если вы правильно настроили свойство, т.е.:

@property (assign) id<BDelegate> delegate;

Вам не нужно выполнять какое-либо управление памятью в Dealloc, так как счетчик сохранения не увеличивается, когда вы вызываете self.b.delegate = self; -- в отличие от использования (сохранить) или (копировать)

Есть смысл? Было бы хорошо установить для делегата значение nil, но какой в этом смысл?

person Josh    schedule 01.07.2009
comment
+1 Правильно, делегатов почти никогда не следует оставлять. Это классическая причина циклов сохранения, поскольку делегат обычно сохраняет делегирующий объект. - person Quinn Taylor; 02.07.2009
comment
Есть смысл установить для делегата значение nil, потому что, если клиент попытается вызвать объект делегата, который больше не существует, он не рухнет, поскольку он установлен на nil. - person Boon; 02.07.2009
comment
Вы освобождаете класс, содержащий делегат... в этот момент вы удаляете экземпляр класса из памяти, вы получите ошибку доступа к памяти, если выполните какое-либо действие над объектом, а тем более обратитесь к своему делегату. Если я что-то пропустил здесь... - person Josh; 02.07.2009
comment
Да, вы, вероятно, получите сообщение об ошибке, если это произойдет, но вы можете предотвратить это, установив для него значение nil. См. Ответ Питера для сценария, в котором это может легко произойти. - person Quinn Taylor; 02.07.2009

Сначала несколько наблюдений...

  1. Вы забыли вызвать [super dealloc] в конце вашего собственного метода Dealloc.
  2. Поскольку 'a' создал 'b' и если никакие другие объекты не сохранили 'b', нет смысла обнулять делегата в -dealloc, так как 'b' все равно будет уничтожен. Если возможно, что другие объекты имеют ссылку на 'b' (это означает, что он может пережить 'a'), установите для делегата значение nil.
  3. Объект 'b' должен заботиться о своем делегате в своем собственном -dealloc если необходимо. (Как правило, делегатор не сохраняет делегата.)
  4. Избегайте использования свойств в методах -init... и -dealloc — Apple не одобряет это, и на то есть веские причины. (Это может привести не только к неожиданным побочным эффектам, но и к более неприятным проблемам.)
  5. Использование свойств (через точечный синтаксис), когда вам не нужно, незаметно добавляет дополнительную работу. Например, self.b.delegate = self эквивалентно [[self getB] setDelegate:self] — это просто синтаксический сахар, из-за которого создается впечатление, что вы обращаетесь к ivar напрямую, но на самом деле это не так.
  6. Использование свойств без понимания того, что они делают, может привести к проблемам. Если self.b сохраняет значение (свойство установлено на «назначить»), у вас есть утечка памяти.

Вот как я, вероятно, написал бы это:

- (void) someFunc {
  b = [[B alloc] init];
  b.delegate = self; // or [b setDelegate:self];
}

- (void) dealloc {
  b.delegate = nil;
  [b release];
  [super dealloc];
}
person Quinn Taylor    schedule 01.07.2009