Когда и зачем мне объявлять локальную переменную как __weak с помощью ARC?

Майк Эш написал это введение в ARC, где он вводит что-то вроде:

__weak Foo *_weakFoo = [object foo];

Зачем мне делать это для локальной временной переменной? __weak — это обнуляющая ссылка, которая автоматически устанавливает указатель _weakFoo на nil, как только объект, на который указывает ссылка, освобождается. Кроме того, __weak доступен только в iOS >= 5.

Когда я столкнусь с проблемами, если просто сделаю это?:

Foo *_weakFoo = [object foo];

Всегда ожидается, что он вернет объект или ноль. Я предполагаю следующее:

Foo *_weakFoo = [object foo];
[self doSomethingStupid]; // does something bad so foo gets deallocated
[_weakFoo doIt]; // CRASH! msg sent to deallocated instance 0x123456

Одна вещь, которая все еще беспокоит меня с ARC, это: когда он узнает, что мне больше не нужен объект? Я бы сказал, что когда я устанавливаю указатель на nil или на что-то еще, он выясняет, что ранее указанный объект больше не нужен этому владельцу и, следовательно, может уйти. Но дело в том, что я установил его на ноль. Так что все равно ноль!

Итак, когда __weak для локальной переменной имеет смысл, и что за безумие я должен сделать где-то еще, чтобы оно мне действительно понадобилось?


person openfrog    schedule 10.01.2012    source источник


Ответы (1)


Я использую __weak локальные переменные, если мне нужно манипулировать self внутри блока, чтобы избежать цикла сохранения. Рассмотрим этот пример, где я использую GCD и блоки для выполнения сетевого запроса строки, а затем устанавливаю ее на метку, объявленную классом, в данном случае TurtlesViewController.

__weak TurtlesViewController *weakSelf = self;
dispatch_queue_t networkQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_async(networkQueue, ^{

    // Kick off a network task for some data that is going to populate a label declared on our class
    NSString *returnString = [networkDataSource retrieveTurtleTime];

    // Dispatch back to the main thread to populate the UILabel
    dispatch_async(dispatch_get_main_queue(), ^{

        // Using self.label here creates a retain cycle. Self owns the block and the block has captured self
        self.label.text = returnString;

        // Instead, we use weakSelf for our reference to the label as it will be torn down by ARC at the end of the loop.
        weakSelf.label.text = returnString;
    });
});
person Mark Adams    schedule 10.01.2012
comment
Как может быть цикл сохранения? - person openfrog; 10.01.2012
comment
@openfrog - этот конкретный блок может не подвергаться наибольшему риску быть циклом сохранения, но тот, с которым я столкнулся, будет наблюдателями на основе блоков для уведомлений (с использованием -addObserverForName:object:queue: usingBlock: NSNotificationCenter). Если вы настроите такой наблюдатель в объекте и обратитесь к чему-то на self, вы установите цикл, поскольку объект удерживает блок, а блок удерживает объект. - person Brad Larson; 11.01.2012
comment
После написания этого я понял, что это не лучший пример, потому что dispatch_async() копирует блок, но только до конца метода. Лучшим примером было бы использование NSBlockOperation, поскольку такой экземпляр владеет переданным блоком в течение всего времени жизни объекта, что повышает вероятность циклов сохранения. - person Mark Adams; 11.01.2012