#objective-c
#objective-c
Вопрос:
Внутри блоков мне нужно передавать некоторую версию self вспомогательным методам?
В этом примере publicMethod() вызывает _privateHelper с использованием strongSelf .
Но _privateHelper() вызывает _secondPrivateHelper() с использованием self . Это нормально или я должен передать strongSelf
помощникам?
- (void)publicMethod
{
auto __weak weakSelf = self;
dispatch_sync(dispatch_get_main_queue(), ^{
auto __strong strongSelf = weakSelf;
if (!strongSelf) {
return;
}
[strongSelf _privateHelper];
});
}
- (void)_privateHelper
{
[self _secondPrivateHelper]; // <-- what version of `self` is sending the message here? I'm assuming the copied `strongSelf` from above.
}
- (void)_secondPrivateHelper
{
NSLog(@"Hello, World");
}
Ответ №1:
TLDR
Когда использовать strong
или weak
зависит от того, как вы используете блок. В вашем коде strong
и weak
на самом деле не имеет значения, но я приведу пример, где это важно, и объясню, как затем использовать weak
.
Я думаю, что вы рассматриваете здесь неправильные вещи. В вашем примере все указатели на self
могут (и должны) быть strong
. Вам не нужно танцевать танец здесь, и вам следует рассмотреть возможность использования weak
только при создании циклов сохранения, о которых я расскажу чуть позже.
В вашем случае обратите внимание, что когда вы находитесь внутри блока, на который вам нужен strong
указатель self
. Если у вас есть weak
указатель, блок становится лишним, когда self
он есть nil
. Чтобы получить сильный указатель, просто измените свой блок на
... ^{
[self _privateHelper];
});
а не так, как вы это делаете. Теперь (и в вашем исходном коде) у вас есть (и должен быть) strong
указатель на self
внутри вашего _privateHelper
и _secondPrivateHelper
.
Но это не проблема.
Сохранить цикл
Предположим, у вас есть ivar
@property (strong,nonatomic) void ( ^ block )( void );
Только сейчас у вас возникли потенциальные проблемы, и вам, вероятно, нужно использовать weak
указатели. Причина в том, что если где-то в вашем коде у вас есть
self.block = ^{ [self _privateHelper]; };
блок сохранит strong
указатель на self
. В то же время self
сохраняет через ivar strong
указатель на блок. Вы видите цикл сохранения? Ни блок, ни self
когда-либо будут освобождены автоматически, поскольку оба сохраняют strong
ссылки на другой.
Исправление
Вы можете изменить ivar на weak
, и в этом случае вы прерываете цикл. Если блок выходит за рамки weak
, в который переходит ivar nil
, и сохранение self
не выполняется.
Вы также могли бы сделать
self.block = ^ { [weakSelf _privateHelper]; };
внутри блока, чтобы разорвать цикл. Теперь, если self
он выходит за рамки, блок больше не удерживает его, и снова цикл сохранения не включается self
.
Используются оба эти исправления — weak
в среде пользовательского интерфейса будет много ivar, чтобы, например, ярлык сохранял strong
ссылку на vc, в то время как vc также сохранял strong
ссылку на ярлык.
При работе с блоками вы обычно используете weak
указатели self
внутри блока, только если у вас потенциально есть цикл сохранения. Часто это не проблема, и в вашем коде также не является проблемой, поскольку self
блок не сохраняется. Так что в целом ваши вспомогательные блоки, вероятно, могли бы уйти с strong
указателями на self
. Вам нужно только танцевать танец, если каким-то образом self
и блок создают цикл сохранения.
Наконец, третий способ исправить это — установить ivar для nil
себя. Итак, у вас может быть код, который выглядит следующим образом
if ( self.block )
{
self.block (); // Execute block
self.block = nil;
}
Иногда это также имеет смысл, например, когда вы хотите сохранить блок и self
пока блок не будет выполнен. Здесь вы освобождаете ivar вручную, устанавливая для nil
него значение, после которого он больше не сохраняется self
, и цикл прерывается, хотя и вручную.
Вы могли бы даже сделать это внутри блока, например, изменить блок на
self.block = ^ { [self _privateHelper]; self.block = nil; /* release when done */ };