Использование __block и __weak

Я прочитал эту тему: Что делает "__ блок" ключевое слово mean?, в котором обсуждается, что используется __block, но я смущен одним из ответов. Он говорит, что __block используется, чтобы избежать циклов сохранения, но комментарии под ним оставляют меня неуверенными.

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

 self.someProperty = x; //where x is some object (id)
 __block __weak VP_User *this = self;

 //begin a callback-style block
     this.someProperty = nil;

Нужно ли использовать как __block, так и __weak? Любые вопиющие проблемы с этим способом выглядят?

Ответ 1

__block - это определитель хранилища. Он указывает, что переменная должна быть непосредственно захвачена блоком, а не копированием. Это полезно, если вам нужно изменить исходную переменную, как в следующем примере

__block NSString *aString = @"Hey!"; 
void(^aBlock)() = ^{ aString = @"Hello!" }; // without __block you couldn't modify aString
NSLog(@"%@", aString); // Hey!
aBlock();
NSLog(@"%@", aString); // Hello!

В ARC это приводит к тому, что переменная автоматически сохраняется, поэтому ее можно безопасно ссылаться в рамках реализации блока. В предыдущем примере затем aString отправляется сообщение retain при захвате в контексте блока.

Не то, чтобы это не подтвердилось в MRC (Manual Reference Counting), где эта переменная указана без сохранения.

Отмечая его как __weak, эта переменная не сохраняется, поэтому блок непосредственно ссылается на нее, но не сохраняет ее. Это потенциально опасно, поскольку в случае, если блок живет дольше, чем переменная, поскольку он будет ссылаться на память мусора (и может произойти сбой).

Здесь соответствующий абзац из clang doc:

В языках Objective-C и Objective-C ++ мы допускаем спецификатор __weak для __block переменных типа объекта. [...] Этот определитель заставляет эти переменные сохраняться без сохранения отправляемых сообщений. Это сознательно приводит к оборванным указателям, если блок (или копия) переживает время жизни этого объекта.

Наконец, утверждение, что __block может использоваться для исключения сильных ссылочных циклов (так называемых циклов сохранения), является неправильным в контексте ARC. В связи с тем фактом, что в ARC __block заставляет переменную строго ссылаться, она, скорее всего, вызывает их.

Например, в MRC этот код прерывает цикл сохранения

__block typeof(self) blockSelf = self; //this would retain self in ARC!
[self methodThatTakesABlock:^ {
    [blockSelf doSomething];
}];

тогда как для достижения такого же результата в ARC вы обычно делаете

__weak typeof(self) weakSelf = self;
[self methodThatTakesABlock:^ {
    [weakSelf doSomething];
}];

Ответ 2

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

например:

__block BOOL result = NO;
dispatch_sync(dispatch_get_main_queue(), ^{
  ...
  result = YES;
  ...
});

Вы должны использовать __weak, если хотите избежать циклов сохранения.

например:.

__weak typeof(self) wself = self;
self.foobarCompletion = ^{
  ...
  wself.foo = YES;
  ...
};

Вы можете комбинировать их, если есть необходимость.