Re: NSDictionary @synchronize to read multiple objects
Re: NSDictionary @synchronize to read multiple objects
- Subject: Re: NSDictionary @synchronize to read multiple objects
- From: Ken Thomases <email@hidden>
- Date: Tue, 01 Apr 2014 00:32:09 -0500
On Apr 1, 2014, at 12:16 AM, Trygve Inda wrote:
> I have an object with a dictionary property defined as:
>
> @property (nonatomic, retain) NSMutableDictionary* myDict;
>
> This gets set when it is received via an AppleEvent from another
> application.
>
>
>
> Multiple threads in the receiving app need to read (the threads never write,
> only read) from the Dictionary like:
>
> [self setValueOne:[[myDict objectForKey:kValueOneKey] intValue]];
> [self setValueTwo:[[myDict objectForKey:kValueTwoKey] intValue]];
>
> I need to be able to set a new myDict and not have it happen between the
> reading of different values from the dictionary by other threads.
>
> Should this be done best with a @synchronize around a copy:
>
> @synchronize(myDict)
> {
> myCopy = [[NSDictionary alloc] initWithDictionary:myDict copyItems:YES];
> }
>
> [self setValueOne:[[myCopy objectForKey:kValueOneKey] intValue]];
> [self setValueTwo:[[myCopy objectForKey:kValueTwoKey] intValue]];
> [myCopy release];
>
> This would seem to ensure that valueOne and valueTwo were obtained from the
> same instance of a given dictionary and that the dictionary did not change
> in between the reading and setting of these two dictionary content objects.
>
> I need to avoid valueOne and valueTwo coming from two different instances of
> myDict.
An @synchronized directive only at the read site accomplishes nothing. The reads need to be synchronized with the setter, so the setter also has to use @synchronized _on the same object_. It also doesn't make sense to synchronize on the myDict object. That's what is in flux. You should synchronize on "self", the object which owns and manages myDict. I wouldn't bother making a copy within the @synchronized block. I'd just read and retain both values you want. Or read and convert to int, so they're no longer objects. You could call the setters within the block if they are not long running and don't synchronize against other locks, but it's probably safest to call the setters outside of the block.
An alternative would be to use GCD to implement synchronization, especially since you can implement a read-write scheme. Create a private concurrent queue. Submit all operations on the myDict property to that queue. For read operations, use dispatch_sync(). For write operations, use dispatch_barrier_async() (you can also use dispatch_barrier_sync(), but you often don't need to care when the write operation completes, so long as all subsequent read operations are guaranteed to see the result of the write). This way, readers can all run concurrently so long as there's no writing happening. A write will not begin until all other operations (read or write) are done and will prevent any other operations from beginning until it completes.
Again, to make sure two values are read without the possibility of the dictionary being changed, do both reads within a single block submitted to the queue.
Regards,
Ken
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden