Слабое свойство не обнуление с помощью ARC - PullRequest
2 голосов
/ 01 февраля 2012

У меня есть следующий простой код для объекта со слабой ссылкой:

// интерфейс

@interface GMWeakRefObj : NSObject
@property (weak) id object;
@end

// реализация

@implementation GMWeakRefObj 
@synthesize object;
@end

Когда я запускаю следующий тестовый код, он не работает на втором утверждении:

NSData* d = [[NSData alloc] init];
GMWeakRefObj* weakRef = [[GMWeakRefObj alloc] init];
weakRef.object = d;
NSAssert(weakRef.object != nil, @"Reference wasn't assigned");
d = nil;
NSAssert(weakRef.object == nil, @"Reference wasn't zeroed"); // <-- FAIL

Разве слабые ссылки ARC не должны обнуляться? И если так, что я делаю не так?

1 Ответ

3 голосов
/ 01 февраля 2012

Попробуйте использовать пользовательский класс вместо NSData для d, например MyData. Реализуйте в нем dealloc метод и установите в нем точку останова. Вы увидите, что dealloc вызывается пулом авто-выпуска после последнего NSAssert. Только после этой недели ссылка станет nil.

ADD: Похоже, я должен расширить свой ответ, чтобы понять, почему это так работает. Сначала давайте рассмотрим ваш пример (из комментариев):

NSData* data = [[NSData alloc] init];
__weak NSData* weakRef = data;
data = nil;
NSAssert(weakRef == nil, @"Failed to zero");

Работает как положено, weakRef становится nil после data = nil. Следующий пример тоже работает:

NSData* data = [[NSData alloc] init];
__weak NSData* weakRef = data;
NSLog(@"%@", data);
data = nil;
NSAssert(weakRef == nil, @"Failed to zero");

Но последний пример не работает:

NSData* data = [[NSData alloc] init];
__weak NSData* weakRef = data;
NSLog(@"%@", weakRef);
data = nil;
NSAssert(weakRef == nil, @"Failed to zero");

Единственное отличие состоит в том, что мы используем слабую ссылку на выходной журнал. Почему?

(остальная часть ответа может быть неправильной :))

Представьте, что вы NSLog (или любая другая функция / селектор, которую мы вызывали до data = nil), полагаются на то, что аргумент не равен nil. Например, он имеет «if (arg == nil) return;» в самом начале.

В многопоточной среде слабая ссылка может стать nil после if.

Так что правильно написанная функция должна выглядеть так:

  // ...
  T* local_arg = arg;   // NOTE: it is strong!
  if (local_arg == nil)
    return;
  // work with local_arg here, not with arg
  // ...

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

Итак, должно быть понятно, почему ваш GMWeakRefObj контрольный пример не работает - weakRef автоматически высвобождается перед вызовом setObject setter.

...