你问:
- 在苹果的文档中,它说对于并发操作,我应该使用
start
、asynchronous
、executing
、finished
等.在我的情况下,我只需要有一个延迟,而不是实际上做任何异步操作,我应该只在Main中做,还是应该通过实现苹果建议的方法作为异步操作来做?
首先,你are在做一些不同步的事情.即,asyncAfter
是异步的.
其次,苹果讨论并发操作背后的动机是,在它启动的异步任务也完成之前,操作不应该结束.您说要取消操作,但只有当您要取消操作时操作仍在运行时,这才有意义.这个特性,在不阻塞线程的情况下将异步任务包装在一个对象中,这是我们使用操作而不仅仅是GCD的关键原因之一.它为异步任务之间的各种优雅依赖打开了大门(依赖、取消等).
- 我的 idea 正确吗,在代码1中,有一个隐含的self 保留,这听起来不正确,可以创建保留循环?
关于强引用周期问题,让我们看一下您的第一个示例.虽然操作的创建者使用[weak self]
捕获列表是谨慎的,但不应该是必需的.操作(或任何使用异步调用闭包的操作)的好设计是让它在不再需要闭包时释放闭包:
class SomeOperation2: Operation {
private var closure: (() -> Void)?
init(closure: @escaping () -> Void) {
self.closure = closure
}
override func main() {
if isCancelled {
return
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: doSomething)
}
override func cancel() {
closure = nil
super.cancel()
}
private func doSomething() {
guard !isCancelled else {
return
}
closure?()
closure = nil
}
}
这并不意味着调用者不应该使用[weak self]
捕获列表,只是操作不再需要它,并且当它完成闭包时将解析任何强引用周期.
[注意,在上面,为了简单起见,我省略了变量的同步.但您需要同步对它的访问,以确保线程安全设计.]
但这种设计回避了一个问题,即为什么你会想要保持asyncAfter
的预定时间,即使你取消了操作,仍然可以开火.最好是通过将闭合包在可以取消的DispatchWorkItem
中来取消它,例如:
class SomeOperation: Operation {
private var item: DispatchWorkItem!
init(closure: @escaping () -> Void) {
super.init()
item = DispatchWorkItem { [weak self] in
closure()
self?.item = nil
}
}
override func main() {
if isCancelled { return }
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: item)
}
override func cancel() {
item?.cancel()
item = nil
super.cancel()
}
}
概述了内存问题之后,我们应该注意到,这可能是没有意义的,因为您可能只应该将其设置为一个并发操作(具有所有的自定义KVO),正如您在文档中所标识的那样.此外,我们在取消逻辑中投入的所有注意事项仅在操作在异步进程完成之前处于活动状态时才适用.因此,我们将进行并发操作.例如,
class SomeOperation: AsynchronousOperation {
private var item: DispatchWorkItem!
init(closure: @escaping () -> Void) {
super.init()
item = DispatchWorkItem { [weak self] in
closure()
self?.item = nil
self?.complete()
}
}
override func main() {
if isCancelled { return }
synchronized {
DispatchQueue.main.asyncAfter(deadline: .now() + 3, execute: item)
}
}
override func cancel() {
super.cancel()
synchronized {
item?.cancel()
item = nil
}
}
}
上面使用了一个异步操作基类,它(A)执行必要的KVO通知;(B)是线程安全的.这里有一个随机的例子,说明如何实现这一点:
/// Asynchronous Operation base class
///
/// This class performs all of the necessary KVN of `isFinished` and
/// `isExecuting` for a concurrent `NSOperation` subclass. So, to developer
/// a concurrent NSOperation subclass, you instead subclass this class which:
///
/// - must override `main()` with the tasks that initiate the asynchronous task;
///
/// - must call `complete()` function when the asynchronous task is done;
///
/// - optionally, periodically check `self.cancelled` status, performing any clean-up
/// necessary and then ensuring that `complete()` is called; or
/// override `cancel` method, calling `super.cancel()` and then cleaning-up
/// and ensuring `complete()` is called.
public class AsynchronousOperation: Operation {
private let lock = NSLock()
private var _executing: Bool = false
override private(set) public var isExecuting: Bool {
get {
synchronized { _executing }
}
set {
willChangeValue(forKey: #keyPath(isExecuting))
synchronized { _executing = newValue }
didChangeValue(forKey: #keyPath(isExecuting))
}
}
private var _finished: Bool = false
override private(set) public var isFinished: Bool {
get {
synchronized { _finished }
}
set {
willChangeValue(forKey: #keyPath(isFinished))
synchronized { _finished = newValue }
didChangeValue(forKey: #keyPath(isFinished))
}
}
override public var isAsynchronous: Bool { return true }
/// Complete the operation
///
/// This will result in the appropriate KVN of isFinished and isExecuting
public func complete() {
if isExecuting {
isExecuting = false
isFinished = true
}
}
public override func cancel() {
super.cancel()
complete()
}
override public func start() {
if isCancelled {
isFinished = true
return
}
isExecuting = true
main()
}
override public func main() {
fatalError("subclasses must override `main`")
}
public func synchronized<T>(block: () throws -> T) rethrows -> T {
try lock.synchronized { try block() }
}
}
extension NSLocking {
public func synchronized<T>(block: () throws -> T) rethrows -> T {
lock()
defer { unlock() }
return try block()
}
}