В блоках Objective-c мне нужно передавать слабое / сильное self помощникам?

#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 */ };