If I have a class property that will likely be getting modified during asynchronous events like API calls, what is the best way to make sure that changing the property while it is being accessed by another thread will not cause a crash?
For mutable objects, you will need some form of mutual exclusion. There are many options, depending on abstraction level and usage. Examples of:
pthread_mutex*
APIs
NSLock
APIs
@synchronized
- semaphores
- dispatch APIs
1) NSLock + atomic property ...but it seems like in this case I would have to lock the property for every read and write, which to me would defeat the purpose of setting it as atomic.
Exactly. If you need to lock every access, Atomic offers nothing. It's very rare that atomic is actually useful (a corner case where a property is so simple and also independent of any other state).
In more detail, you have mentioned NSArray
. If that is a copy
property (and it should be), then atomic could in rare cases allow you to safely get/set the array by immutable copy in practice. However, it's not very useful in practice to have a class which is no more than a pointer to an immutable array instance; usually, you want to do something with that array, and usually you want to interact with the objects in a thread safe manner. The implication there is the lock in question can also be used for mutual exclusion to the array's elements (if done correctly).
So where do you need to lock to guarantee mutual exclusion of an NSMutableArray
ivar? When setting, when getting, and nearly every time you message it. Even asking its -count
or for its elements should involve a lock to eliminate any race condition. Of course, you can wrap this up in higher level operations for correctness and do those operations -- acquiring the lock once.
2) Nonatomic property
Atomic will not save you, nor will nonatomic. Atomic only saves you from some potential race conditions in this scenario. Therefore, you should generally use nonatomic because you already need to introduce full mutual exclusion to guarantee there is no race condition.
Would a call back to a delegate after a successful API response be on the thread opened for that API call, or would it be back on the main thread?
That depends on the API.
And if it's on a different thread, could I put it back on the main thread?
Yes, you could add it to the run loop of the main thread or use the dispatch queue. That's 'kludgey', unless the work needs to happen on a specific thread -- most obvious case is when updating an AppKit or UIKit view.