Question

I have an app (using retain/release, not GC) that maintains an NSArray instance variable, which is exposed as a property like so:

@interface MyObject : NSObject
{
    NSArray* myArray;
}
@property (copy) NSArray* myArray;
@end

I want to access the contents of this array from a secondary thread, which is detached using -performSelectorInBackground:withObject:. It is possible and indeed likely that the array will change during the execution of the secondary thread.

In the secondary thread I want to do something like this:

if([self.myArray containsObject:foo])
{
    //do stuff
}

From reading the threading documentation, it seems I should be able use the @synchronized directive in the accessors like so:

@implementation MyObject
- (NSArray *)myArray
{
    NSArray *result;
    @synchronized(self)
    {
        result = [myArray retain];
    }
    return [result autorelease];
}

- (void)setMyArray:(NSArray *)aMyArray
{
    @synchronized(self)
    {
        [myArray release];
        myArray = [aMyArray copy];
    }
}
@end

Is this all I need to do to ensure thread safety, or is it more complex?

Update: I've subsequently found a great article on Apple's site that addresses this issue in depth: http://developer.apple.com/mac/library/technotes/tn2002/tn2059.html

Was it helpful?

Solution

Your code above protects you from setting the array concurrently, or getting the array while another is setting it. Since it is a non-mutable array, this protects the array itself just fine.

However, if by "the array will change" you mean you'll be editing the items inside the array, you could still have some problems. For example, if the array was filled with NSMutableStrings, and you had a thread that ran:

NSMutableString *foo = [myObject.myArray objectAtIndex:0];
[foo appendString:@"foo"];

and another that ran

NSMutableString *bar = [myObject.myArray objectAtIndex:0];
[bar appendString:@"bar"];

The access to the array would be safe (one thread would have to wait for the other to access it), however, access to the foo/bar pointer (which is the same) would not be, since both calls to 'appendString' are outside of the @synchronized block.

If this is how your array will change, you'll need to synchronize these points of access as well. Either with more @synchronized blocks, or other types of locks. See Using Locks

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