Question

Say I have a class:

@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

I understand that, in the dispatch_async block, self is being retained because of the _widget iVar. So let's say that some other non-main thread calls foo while the block on the main thread is being processed. All of a sudden, my block has it's _widget reference ripped out from underneath it and I get some EXC_BAD_ACCESS (SIGSEGV).

At first blush, I thought I would say:

Widget* localWidget = _widget;
dispatch_async(dispatch_get_main_queue(), ^{
    [localWidget someMethodWithData:data];
});

But, this pattern becomes cumbersome if I have many iVars I need to make local.

I would like to know what I can do to avoid this situation without writing a bunch of boilerplate code?

Was it helpful?

Solution

Accessing an instance variable from two separate threads at the same time is not safe. You need some kind of synchronization mechanism such as property or @synchronized block. For example:

@property(strong, readwrite) Widget* widget;

(Note the lack of nonatomic.)

And access the value via the property:

@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

Of course, if foo is called asynchronously, your self.widget could return nil. If this is not the behavior you want, your proposed local-variable solution is the way to go.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top