NSLock +原子属性vs非原子

jra*_*ede 9 multithreading locking objective-c thread-safety foundation

我对目标C相当新.如果我有一个类属性可能会在API调用等异步事件期间被修改,那么确保在另一个线程访问属性时更改属性的最佳方法是什么?导致崩溃?

据我所知,我有两个选择:

1)NSLock +原子属性

...但似乎在这种情况下我必须为每次读取和写入锁定属性,这对我来说会破坏将其设置为原子的目的.

2)非原子性质

我也可以把它设置为非原子的,但是我想我必须在主线程上做所有的读/写操作.有没有办法通过API调用来做到这一点?成功的API响应之后,如果在为该API调用打开的线程上回调给委托,或者它是否会返回主线程?如果它在不同的线程上,我可以把它放回主线程吗?具体来说,我担心NSArray会在另一个线程循环通过时发生变化.

这样做的最佳方式是什么?

Cou*_*per 4

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;
    });
}
Run Code Online (Sandbox Code Playgroud)

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;
    }     
}
Run Code Online (Sandbox Code Playgroud)

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.

Note:

If the shared resource will be accessed by UIKit, the sync_queue shall be the main queue.