Использование __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;
...
};
Вы можете объединить их, если есть необходимость.