Swift - 正在检查弱变量是否为 nil 或不是线程安全的?

Swift - Is checking whether a weak variable is nil or not thread-safe?

我有一个运行很长时间的进程,我希望能够中断它。

func longProcess (shouldAbort: @escaping ()->Bool) {

    // Runs a long loop and periodically checks shouldAbort(),
    // returning early if shouldAbort() returns true

}

这是我的 class 使用它的:

class Example {

    private var abortFlag: NSObject? = .init()

    private var dispatchQueue: DispatchQueue = .init(label: "Example")

    func startProcess () {
        let shouldAbort: ()->Bool = { [weak abortFlag] in
            return abortFlag == nil
        }

        dispatchQueue.async {
            longProcess(shouldAbort: shouldAbort)
        }
    }

    func abortProcess () {
        self.abortFlag = nil
    }
}

shouldAbort 闭包捕获对 abortFlagweak 引用,并检查该引用是指向 nil 还是指向 NSObject。由于引用是 weak,如果原来的 NSObject 被释放,那么闭包捕获的引用将突然变为 nil,闭包将开始返回 true。在私有 dispatchQueue 上发生的 longProcess 函数期间将重复调用闭包。 Example class 上的 abortProcess 方法将从其他队列外部调用。如果有人调用 abortProcess(),从而取消分配 abortFlag,同时 longProcess 正在尝试执行检查以查看 abortFlag 是否已被取消分配,该怎么办?检查 myWeakReference == nil 是线程安全操作吗?

您可以将分派任务创建为 DispatchWorkItem,它有一个 thread-safe isCancelled property already. You can then dispatch that DispatchWorkItem to a queue and have it periodically check its isCancelled. You can then just cancel 分派任务,您想要停止它。


或者,当试图将一些工作包装在一个对象中时,我们通常会使用 Operation,相反,它将任务很好地封装在它自己的 class 中:

class SomeLongOperation: Operation {
    override func main() {
        // Runs a long loop and periodically checks `isCancelled`

        while !isCancelled {
            Thread.sleep(forTimeInterval: 0.1)
            print("tick")
        }
    }
}

并创建队列并将操作添加到该队列:

let queue = OperationQueue()
let operation = SomeLongOperation()
queue.addOperation(operation)

并取消操作:

operation.cancel()

或者

queue.cancelAllOperations()

底线,无论您是使用 Operation(坦率地说,这是将某些任务包装在其自己的对象中的“go-to”解决方案)还是 roll-your-own 和 DispatchWorkItem,想法是一样的,即你不需要有自己的状态 属性 来检测任务的取消。调度队列和操作队列都已经有了很好的机制来为你简化这个过程。

我看到这个错误 (Weak properties are not thread safe when reading SR-192) 表明弱引用读取不是线程安全的,但它已被修复,这表明(在运行时没有任何错误),弱引用读取是有意的线程安全。

也很有趣:Friday Q&A 2017-09-22: Swift 4 Weak References by Mike Ash