I'm fairly new to objective C. 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?
As far as I can tell I have two options:
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.
2) Nonatomic property
I could also set it as nonatomic, but then I think I would have to do all reading/writing on the main thread. Is there a way of doing that as a result of an API call? 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? And if it's on a different thread, could I put it back on the main thread? Specifically, I'm worried about an NSArray getting changed while another thread is looping through it.
What is the best way of doing this?
Atomic means only one thread accesses the variable (static type). Atomic is thread-safe, but it is slow. Nonatomic means multiple threads access the variable (dynamic type). Nonatomic is thread-unsafe, but it is fast.
Swift has no such specifier. In Objective-C the implementation of an atomic property allows properties to be safely read and written from different threads. For nonatomic properties, the underlying pointer of a read value could be released when a new value is being written at the same time.
Non atomic properties has no guarantee regarding the returned value . It can be the correct value, a partially written value or even some garbage value. As most things that are not safe — this comes with enhanced speed of accessing this properties.
Atomic:- is the default behavior. it will ensure the present process is completed by the CPU, before another process accesses the variable.it is not fast, as it ensures the process is completed entirelyNon-Atomic: - is NOT the default behavior.
I would like to grab justin's option "dispatch APIs" for a short example:
Concurrent access to shared resources can be made safe through executing all accesses on a dedicated serial queue, lets call it "sync_queue".
This "sync_queue" will likely be a private queue of the class whose ivars is the resource you want to modify.
You may now define a read/write nonatomic property, for example:
@propery (nonatomic) NSArray* array;
The write access can be implemented as shown below:
- (void) setArray:(NSArray* newValue)
{
dispatch_async(sync_queue, ^{
_array = newValue;
});
}
Note that a write access is asynchronous.
The read access to the property will be implemented as follows:
- (NSArray*) array:(NSArray* value)
{
if (dispatch_get_specific(SyncQueueID) == sync_queue_id)) {
return _array;
}
else {
__block NSArray* result = nil;
dispatch_sync(_sync_queue, ^{
result = _array;
});
return result;
}
}
Unlike a write access, a read access requires to be synchronous. The method also has to check if the current execution context is not already the sync_queue or a child or any grand children of the sync queue - otherwise, the read access would cause a dead lock.
To identify the current execution context we associate a particular identifier with the sync queue, using function dispatch_queue_set_specific() when creating it. Later we use dispatch_get_specific to obtain this identifier from the current queue or from the parent or any grand parent queue. If it returns this particular identifier, the method is executing on the sync queue respectively on a child queue or any grand children. If that's true, the method returns the value immediately. Otherwise, it synchronously schedules on the sync queue.
If the shared resource will be accessed by UIKit, the sync_queue shall be the main queue.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With