首页 > 解决方案 > Swift - 检查弱变量是否为 nil 或不是线程安全的?

问题描述

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

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

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

}

这是我的班级使用它:

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闭包捕获对 的引用weakabortFlag并检查该引用是指向nil还是指向NSObject。由于引用是weak,如果原始NSObject对象被释放,那么闭包捕获的引用将突然被释放,nil并且闭包将开始返回true。闭包将在longProcess函数期间重复调用,这发生在 private 上dispatchQueue。类上的abortProcess方法Example将从其他队列外部调用。如果有人在尝试执行检查以查看是否已被解除分配的同时调用abortProcess(),从而解除分配怎么办?是否检查线程安全操作?abortFlaglongProcessabortFlagmyWeakReference == nil

标签: swiftqueuethread-safetyweak-referencesabort

解决方案


您可以将分派任务创建为DispatchWorkItem,它已经具有线程安全isCancelled属性。然后,您可以将其分派DispatchWorkItem到队列并让它定期检查其isCancelled. 然后,您可以只cancel发送您想要停止它的点。


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

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(坦率地说,将某些任务包装在自己的对象中的“首选”解决方案)还是自己滚动使用DispatchWorkItem,这个想法都是一样的,即您不需要拥有自己的 state 属性来检测任务的取消。调度队列和操作队列都已经有了很好的机制来为你简化这个过程。


推荐阅读