Есть ли способ заблокировать объект в Swift, как в С#

У меня есть следующий код:

func foo() {
    var sum = 0
    var pendingElements = 10

    for i in 0 ..< 10 {
        proccessElementAsync(i) { value in
            sum += value
            pendingElements--

            if pendingElements == 0 {
                println(sum)
            }
        }
    }
}

В этом случае функция proccessElementAsync, как показывает ее имя, обрабатывает свой входной параметр асинхронно, а когда он заканчивается, он вызывает соответствующий обработчик завершения.

Неудобство с этим подходом состоит в том, что, поскольку переменная pendingElements доступна через несколько потоков, возможно, что оператор if pendingElements == 0 никогда не будет иметь значение true.

В С# мы можем сделать что-то вроде:

Object lockObject = new Object();
...

lock (lockObject) {
    pendingElements--;

    if (pendingElements == 0) {
        Console.WriteLine(sum);
    }
}

и это гарантирует, что эта переменная будет доступна только для потока одновременно. Есть ли способ получить такое же поведение в Swift?

Ответ 1

Надеюсь, это поможет вам.

func lock(obj: AnyObject, blk:() -> ()) {
    objc_sync_enter(obj)
    blk()
    objc_sync_exit(obj)
}

var pendingElements = 10

func foo() {
    var sum = 0
    var pendingElements = 10

    for i in 0 ..< 10 {
        proccessElementAsync(i) { value in

            lock(pendingElements) {
                sum += value
                pendingElements--

                if pendingElements == 0 {
                    println(sum)
                }
            }

        }
    }
}

Ответ 2

Нет встроенных инструментов блокировки, но есть обходные пути, подобные объяснению в этом вопросе SO:

Что такое Swift, эквивалентный Objective-C 's синхронизированному "?

Используя один из ответов, вы можете создать функцию:

    func synchronize(lockObj: AnyObject!, closure: ()->()){
        objc_sync_enter(lockObj)
        closure()
        objc_sync_exit(lockObj)
    }

а затем:

     func foo() {
        var sum = 0
        var pendingElements = 10

        for i in 0 ..< 10 {
            processElementAsync(i) { value in

                synchronize(pendingElements) {
                    sum += value
                    pendingElements--

                    if pendingElements == 0 {
                        println(sum)
                    }
                }

            }
        }
    }