Предотвращение неожиданного освобождения в асинхронных блоках

Скажем, у меня есть класс:

@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. Если это не то поведение, которое вам нужно, предложенное вами решение с локальной переменной - это то, что вам нужно.

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