Слабая личность в блоках

Нужно ли мне проверять, является ли слабое «я» нулевым в блоках?

Я создаю указатель weakSelf, например:

__weak typeof(self) weakSelf = self;

и в начале блоков делаю

if(!weakSelf){return;}

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


person EralpB    schedule 17.06.2015    source источник
comment
Это зависит от того, что должен делать ваш блок. В общем, если вы хотите убедиться, что блок выполняется только тогда, когда wealSelf не равен нулю, вам нужна эта строка!   -  person Nils Ziehn    schedule 17.06.2015
comment
в принципе, любой вызов метода является допустимым операндом для указателя nil в Obj-C, поэтому проверять его не нужно; например если weakself равно nil, то такой вызов метода не вызовет проблем в вашем блоке: [weakself doSomething];.   -  person holex    schedule 17.06.2015


Ответы (3)


В этой проверке нет необходимости, и она дает вам ложное чувство безопасности.

Вот проблема:

__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
    if (!weakSelf) { return; }
    // THE LINE OF INTEREST
    [weakSelf doSomething];
});

В THE LINE OF INTEREST какой-то другой поток может очистить последнюю сильную ссылку на self, после чего weakSelf устанавливается в nil. Таким образом, сообщение doSomething отправляется на nil, что является «безопасным» (оно ничего не делает), но может оказаться не таким, как вы ожидали!

Хуже, если вы хотите предпринять другое действие, когда weakSelf равно нулю, например.

__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
    if (weakSelf) {
        [weakSelf doSomething];
    } else {
        [someOtherObject doSomethingElse];
    }
});

В этом случае между моментом, когда блок проверяет, что weakSelf не является nil, и временем, когда он отправляет сообщение doSomething, weakSelf может стать nil, и ни doSomething, ни doSomethingElse фактически не будут выполняться.

Правильное решение такое:

__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
    typeof(self) strongSelf = weakSelf;
    if (strongSelf) {
        [strongSelf doSomething];
    } else {
        [someOtherObject doSomethingElse];
    }
});

В этом случае копирование weakSelf в strongSelf (по умолчанию сильное) является атомарным. Если weakSelf было нулем, strongSelf будет нулем. Если weakSelf не было nil, strongSelf не будет nil и будет строгой ссылкой на объект, предотвращая его освобождение до сообщения doSomething.

person rob mayoff    schedule 17.06.2015
comment
В вашем коде можно просто использовать self вместо weakSelf. - person Amin Negm-Awad; 17.06.2015
comment
Если вы используете self непосредственно в блоке, блок сильно захватывает self, сохраняя его живым. Если вы используете weakSelf, блок не захватывает сильно self и позволяет освободить его до запуска блока. - person rob mayoff; 17.06.2015
comment
Это правильно. Так объект просто живет дольше. Пока блок должен быть выполнен. Обычно это именно то, что вы хотите. - person Amin Negm-Awad; 17.06.2015
comment
Но иногда это не так. Совет «просто использовать self вместо weakSelf» требует уточнения: иногда это меняет поведение программы. Кроме того, в случае, когда блок сохраняется на неопределенный срок, он может создать цикл сохранения. Этого не произойдет в коде моего ответа, но некоторые часто используемые API (например, NSNotificationCenter) сохраняют блок на неопределенный срок. EralpB не показал контекст, в котором он создает свой блок. В его случае использования может потребоваться избежать цикла сохранения. - person rob mayoff; 17.06.2015
comment
Я не сказал без слов: просто используйте self вместо weakSelf. Я сказал: В вашем коде можно просто использовать self вместо weakSelf. Так что нет смысла искать примеры для чего-то, что не является предметом обсуждения. - person Amin Negm-Awad; 17.06.2015
comment
Этот пример также является отличной аналогией для понимания захвата [weak self] в замыканиях Swift. Ваше правильное решение по сути такое же, как выполнение дополнительной цепочки if let else для self в рамках быстрого закрытия. - person Alex Chase; 01.08.2018

Это кажется совершенно ненужным, поскольку вызов сообщения на nil не является операцией. (Ничего не произошло)

^{
    [weakSelf doSomething]; //Does nothing if weakSelf is nil
}

Единственная причина, по которой я могу предположить, что вы можете захотеть это сделать, заключается в том, что другие сообщения (не для себя) не должны вызываться

^{
    // Here I don't want to add weakSelf as an observer if it's nil
    if (!weakSelf) return;

    [OtherClass addObserverForSomething:weakSelf];
}
person James Webster    schedule 17.06.2015

Слабые ссылки не сохраняют указанный объект. Если никто другой не сохраняет его, объект освобождается, а слабые ссылки ссылаются на nil.

Поэтому возможно, что ваш код выполняется с weakSelf, который ссылается на nil. Но это вовсе не повод его проверять. Особенно в Objective-C вы используете определенное поведение, если отправляете сообщение nil. т. е. это идеальный код, если вы устанавливаете свойство, используя ссылку nil. Просто уходит в никуда.

Конечно, иногда вы не хотите взаимодействовать с nil. В таком случае вы должны проверить его.

Кстати: вам нужно weakSelf только в некоторых, очень редких случаях. Это городская легенда, что в замыканиях общие ссылки на self должны быть слабыми, чтобы предотвратить циклы удержания. Это было неправдой, это неправда и никогда не будет правдой.

person Amin Negm-Awad    schedule 17.06.2015