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

Я прочитал эту тему: что означает ключевое слово "__block"? что обсуждает что __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? Какие-либо явные проблемы с этим выглядит?

2 ответа

Решение

__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 (ручной подсчет ссылок), где на переменную ссылаются без сохранения.

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

Вот соответствующий абзац из Clang Doc:

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

Наконец, утверждение о том, что __block может использоваться, чтобы избежать сильных ссылочных циклов (иначе говоря, сохраняющих циклы) в контексте 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];
}];

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

например:

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

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

например:

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

Вы можете объединить их, если есть необходимость.

Другие вопросы по тегам