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
闭包捕获对 abortFlag
的 weak
引用,并检查该引用是指向 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
我有一个运行很长时间的进程,我希望能够中断它。
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
闭包捕获对 abortFlag
的 weak
引用,并检查该引用是指向 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