Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create thread safe array in Swift

I have a threading problem in Swift. I have an array with some objects in it. Over a delegate the class gets new objects about every second. After that I have to check if the objects are already in the array, so I have to update the object, otherwise I have to delete / add the new object.

If I add a new object I have to fetch some data over the network first. This is handelt via a block.

Now my problem is, how to I synchronic this tasks?

I have tried a dispatch_semaphore, but this one blocks the UI, until the block is finished.

I have also tried a simple bool variable, which checks if the block is currently executed and skips the compare method meanwhile.

But both methods are not ideal.

What's the best way to manage the array, I don't wanna have duplicate data in the array.

like image 619
patrickS Avatar asked Jan 28 '15 11:01

patrickS


People also ask

How do you make an array thread-safe in Swift?

Intrinsically, Arrays in Swift are not thread-safe. Arrays are 'value-typed' in the effect of assignment, initialization, and argument passing — creates an independent instance with its own unique copy of its data. Immutable arrays (declared using let) are thread-safe since it is read-only.

Is Swift set thread-safe?

Unfortunately most of the Swift data types are not thread safe by default, so if you want to achieve thread-safety you usually had to work with serial queues or locks to guarantee the mutual exclusivity of a given variable.

What is thread-safe and non thread-safe Swift?

Thread safe code can be safely called from multiple threads or concurrent tasks without causing any problems (data corruption, crashing, etc). Code that is not thread safe must only be run in one context at a time. An example of thread safe code is let a = ["thread-safe"].


2 Answers

Update for Swift

The recommended pattern for thread-safe access is using dispatch barrier:

let queue = DispatchQueue(label: "thread-safe-obj", attributes: .concurrent)  // write queue.async(flags: .barrier) {     // perform writes on data }  // read var value: ValueType! queue.sync {     // perform read and assign value } return value 
like image 69
skim Avatar answered Oct 15 '22 06:10

skim


I don't know why people take such complex approaches to such a simple thing

  • Don't abuse DispatchQueues for locking. Using queue.sync is nothing more than acquiring a lock and dispatching work to another thread while the lock (DispatchGroup) waits. It is not just unnecessary, but also can have side effects depending on what you are locking. You can look it up yourself in the GCD Source.

    Also GCD does not mix well with the new structured concurrency APIs!

  • Don't use objc_sync_enter/exit, those are used by ObjCs @synchronized which will implicitly bridge Swift collections to a ObjC counterpart, which is also unnecessary. And it is a legacy API.

Just define a lock, and guard your collection access.

var lock = NSLock() var a = [1, 2, 3]  lock.lock() a.append(4) lock.unlock() 

If you want to make your life a bit easier, define a small extension.

extension NSLock {      @discardableResult     func with<T>(_ block: () throws -> T) rethrows -> T {         lock()         defer { unlock() }         return try block()     } }  let lock = NSLock() var a = [1, 2, 3]  lock.with { a.append(4) } 

You can also define a @propertyWrapper to make your member vars atomic.

@propertyWrapper struct Atomic<Value> {      private let lock = NSLock()     private var value: Value      init(default: Value) {         self.value = `default`     }      var wrappedValue: Value {         get {             lock.lock()             defer { lock.unlock() }             return value         }         set {             lock.lock()             value = newValue             lock.unlock()         }     } } 

Last but not least for primitive atomic types there is also Swift Atomics

like image 20
Era Avatar answered Oct 15 '22 06:10

Era