Предотвращение неожиданного освобождения в асинхронных блоках
Скажем, у меня есть класс:
@interface MyClass : NSObject
@property(strong, nonatomic, readwrite) Widget* widget;
-(void)handleData:(NSData*)data;
-(void)foo;
@end
@implementation MyClass
-(void)handleData:(NSData*)data {
//...do a bunch of handleData then...
dispatch_async(dispatch_get_main_queue(), ^{
[_widget someMethodWithData:data];
});
}
-(void)foo {
//...do a bunch of foo then...
_widget = nil;
}
@end
Я понимаю, что в dispatch_async
блок, self
сохраняется из-за _widget
Ивар. Итак, скажем, что некоторые другие неосновные вызовы потока foo
пока блок в основном потоке обрабатывается. Внезапно, мой блок имеет это _widget
ссылка вырвана из-под него, и я получаю некоторые EXC_BAD_ACCESS (SIGSEGV)
,
На первый взгляд я подумал, что скажу:
Widget* localWidget = _widget;
dispatch_async(dispatch_get_main_queue(), ^{
[localWidget someMethodWithData:data];
});
Но этот шаблон становится громоздким, если у меня много iVars, которые мне нужно сделать локальными.
Я хотел бы знать, что я могу сделать, чтобы избежать этой ситуации, не написав кучу шаблонного кода?
1 ответ
Доступ к переменной экземпляра одновременно из двух отдельных потоков небезопасен. Вам нужен какой-то механизм синхронизации, такой как свойство или @synchronized
блок. Например:
@property(strong, readwrite) Widget* widget;
(Обратите внимание на отсутствие неатома.)
И получить доступ к значению через свойство:
@implementation MyClass
-(void)handleData:(NSData*)data {
//...do a bunch of handleData then...
dispatch_async(dispatch_get_main_queue(), ^{
[self.widget someMethodWithData:data];
});
}
-(void)foo {
//...do a bunch of foo then...
self.widget = nil;
}
@end
Конечно, если foo вызывается асинхронно, ваш self.widget может вернуть nil. Если это не то поведение, которое вам нужно, предложенное вами решение с локальной переменной - это то, что вам нужно.