2

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?

Tim Reddy
  • 4,120
  • 1
  • 34
  • 71

1 Answers1

1

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.

Jesse Rusak
  • 54,013
  • 12
  • 95
  • 102
  • 1
    Property atomicity is not synonymous with an object’s thread safety. – Tim Reddy Mar 02 '14 at 03:39
  • Reading and writing a pointer variable is probably already "atomic" on most systems anyway. Your example of atomic property does not solve the thread safety problem, because `self.widget` could be set to `nil` on another thread *between* when `self.widget` is evaluated *and* when you send a message to it. Then it would be non-`nil`, but when you send a message to it it will be sending to an invalid object. Unless you are relying on the strong atomic synthesized getter to autorelease it or pass ownership of it to the calling function. – newacct Mar 02 '14 at 06:14
  • @newacct The issue is not the atomicity of reading/writing the pointer, but of the retain/releases. The atomic property *does* solve this problem, because the whole point of (strong) atomic accessors is to provide the guarantee that the returned object is retained correctly (i.e. the getter will pass ownership or autorelease). See, for example, this comment and answer by Objective-C runtime developer bbum: http://stackoverflow.com/questions/588866/whats-the-difference-between-the-atomic-and-nonatomic-attributes#comment30063314_589392 – Jesse Rusak Mar 02 '14 at 16:59
  • @JesseRusak I think that link in your comment really opened my eyes. It makes me feel that I've been doing asynchronous stuff wrong for all these years... – Tim Reddy Mar 02 '14 at 18:56
  • @TimReddy Well, glad it was helpful! You're definitely right, though, that there could be other thread-safety issues that aren't solved by using an atomic property. – Jesse Rusak Mar 02 '14 at 21:10