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.


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

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

  • 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


Kirsteins's answer is correct, but for convenience, I've updated that answer with Amol Chaudhari and Rob's suggestions for using a concurrent queue with async barrier to allow concurrent reads but block on writes.

I've also wrapped some other array functions that were useful to me.

public class SynchronizedArray<T> {
private var array: [T] = []
private let accessQueue = dispatch_queue_create("SynchronizedArrayAccess", DISPATCH_QUEUE_CONCURRENT)

public func append(newElement: T) {
    dispatch_barrier_async(self.accessQueue) {
        self.array.append(newElement)
    }
}

public func removeAtIndex(index: Int) {
    dispatch_barrier_async(self.accessQueue) {
        self.array.removeAtIndex(index)
    }
}

public var count: Int {
    var count = 0

    dispatch_sync(self.accessQueue) {
        count = self.array.count
    }

    return count
}

public func first() -> T? {
    var element: T?

    dispatch_sync(self.accessQueue) {
        if !self.array.isEmpty {
            element = self.array[0]
        }
    }

    return element
}

public subscript(index: Int) -> T {
    set {
        dispatch_barrier_async(self.accessQueue) {
            self.array[index] = newValue
        }
    }
    get {
        var element: T!

        dispatch_sync(self.accessQueue) {
            element = self.array[index]
        }

        return element
    }
}
}

UPDATE This is the same code, updated for Swift3.

public class SynchronizedArray<T> {
private var array: [T] = []
private let accessQueue = DispatchQueue(label: "SynchronizedArrayAccess", attributes: .concurrent)

public func append(newElement: T) {

    self.accessQueue.async(flags:.barrier) {
        self.array.append(newElement)
    }
}

public func removeAtIndex(index: Int) {

    self.accessQueue.async(flags:.barrier) {
        self.array.remove(at: index)
    }
}

public var count: Int {
    var count = 0

    self.accessQueue.sync {
        count = self.array.count
    }

    return count
}

public func first() -> T? {
    var element: T?

    self.accessQueue.sync {
        if !self.array.isEmpty {
            element = self.array[0]
        }
    }

    return element
}

public subscript(index: Int) -> T {
    set {
        self.accessQueue.async(flags:.barrier) {
            self.array[index] = newValue
        }
    }
    get {
        var element: T!
        self.accessQueue.sync {
            element = self.array[index]
        }

        return element
    }
}
}

My approach to this problem was using serial dispatch queue, to synchronise access to boxed array. It will block the thread when you try to get the value at index and queue is really busy, but that's the problem with locks as well.

public class SynchronizedArray<T> {
    private var array: [T] = []
    private let accessQueue = dispatch_queue_create("SynchronizedArrayAccess", DISPATCH_QUEUE_SERIAL)

    public func append(newElement: T) {
        dispatch_async(self.accessQueue) {
            self.array.append(newElement)
        }
    }

    public subscript(index: Int) -> T {
        set {
            dispatch_async(self.accessQueue) {
                self.array[index] = newValue
            }
        }
        get {
            var element: T!

            dispatch_sync(self.accessQueue) {
                element = self.array[index]
            }

            return element
        }
    }
}

var a = SynchronizedArray<Int>()
a.append(1)
a.append(2)
a.append(3)

// can be empty as this is non-thread safe access
println(a.array)

// thread-safe synchonized access
println(a[0])
println(a[1])
println(a[2])