NSOperation Queue waitUntilAllOperationsAreFinished 导致 NSOperation 实例 deinit 延迟
NSOperation Queue waitUntilAllOperationsAreFinished causes NSOperation instance deinit late
我们的应用目前使用 NSOperation
(Swift 中的 Operation
)来管理网络请求和数据解析的序列。
一些代码需要在队列中的所有 5 个操作完成后执行,通常使用 GCD 组实现。
DispatchQueue.global().async {
(0...5).forEach(){
self.queue.addOperation(CustomOperation(value: [=10=]))
}
self.queue.waitUntilAllOperationsAreFinished()
print("All Tasks Done")
}
问题是 NSOperation
实例不是 deinit
直到完成所有 5 个操作,这导致内存释放比预期的晚。
如果 queue.waitUntilAllOperationsAreFinished
被删除,实例将立即 deinit
。
我们添加了自动释放池来避免它。但是是否可以在使用 waitUntilAllOperationsAreFinished
时立即创建 NSOperation
实例 deinit
?
打印 waitUntilAllOperationsAreFinished
Begin Task 5
Begin Task 4
Begin Task 3
Begin Task 2
Begin Task 1
Begin Task 0
Finish Task 0
Finish Task 1
Finish Task 2
Finish Task 3
Finish Task 4
Finish Task 5
deinit 0
deinit 1
deinit 2
deinit 3
deinit 4
deinit 5
All Tasks Done
不打印 waitUntilAllOperationsAreFinished
All Tasks Done
Begin Task 0
Begin Task 1
Begin Task 4
Begin Task 3
Begin Task 5
Finish Task 0
Begin Task 2
deinit 0
Finish Task 1
deinit 1
Finish Task 2
deinit 2
Finish Task 3
deinit 3
Finish Task 4
deinit 4
Finish Task 5
deinit 5
自定义操作。
class CustomOperation: Operation {
public enum State {
case ready
case running
case finished
}
private var state: State = .ready
override var isAsynchronous: Bool { return true }
override open var isExecuting: Bool { state == .running }
override open var isFinished: Bool { state == .finished }
var value: Int = 0
init(value: Int) {
super.init()
self.value = value
}
override func main() {
print("Begin Task \(value)")
DispatchQueue.global().asyncAfter(deadline: .now()+DispatchTimeInterval.seconds(value)) {
print("Finish Task \(self.value)")
self.finish()
}
}
func finish() {
willChangeValue(forKey: "isExecuting")
willChangeValue(forKey: "isFinished")
state = .finished
didChangeValue(forKey: "isFinished")
didChangeValue(forKey: "isExecuting")
}
deinit {
print("deinit")
}
}
比等待更好的方法是例如观察操作计数
import Combine
DispatchQueue.global().async {
(0...5).forEach {
queue.addOperation(CustomOperation(value: [=10=]))
}
}
var store : AnyCancellable?
store = queue.publisher(for: \.operationCount)
.sink { value in
if value == 0 { print("All Tasks Done")}
}
我建议用Combine,传统KVO也可以
不知道这种行为,但我认为您不能在这里做某事。
如果您担心操作顺序,您可以将队列的 maxConcurrentOperationCount
设置为 1,这样您就可以保持顺序。
如果您关心内存并且您有一些巨大的数据,您可以在 finish()
方法中摆脱它或使用 completionBlock
来传递它。
还有在 OperationQueue
属性上使用 KVO 的选项,它的大部分属性都符合 KVO 和 KVC 标准,您可以对其中一些设置观察以触发回调。
如果您正在部署目标 >=13,您可以使用 vadian 已经编写的 Combine。
我们的应用目前使用 NSOperation
(Swift 中的 Operation
)来管理网络请求和数据解析的序列。
一些代码需要在队列中的所有 5 个操作完成后执行,通常使用 GCD 组实现。
DispatchQueue.global().async {
(0...5).forEach(){
self.queue.addOperation(CustomOperation(value: [=10=]))
}
self.queue.waitUntilAllOperationsAreFinished()
print("All Tasks Done")
}
问题是 NSOperation
实例不是 deinit
直到完成所有 5 个操作,这导致内存释放比预期的晚。
如果 queue.waitUntilAllOperationsAreFinished
被删除,实例将立即 deinit
。
我们添加了自动释放池来避免它。但是是否可以在使用 waitUntilAllOperationsAreFinished
时立即创建 NSOperation
实例 deinit
?
打印 waitUntilAllOperationsAreFinished
Begin Task 5
Begin Task 4
Begin Task 3
Begin Task 2
Begin Task 1
Begin Task 0
Finish Task 0
Finish Task 1
Finish Task 2
Finish Task 3
Finish Task 4
Finish Task 5
deinit 0
deinit 1
deinit 2
deinit 3
deinit 4
deinit 5
All Tasks Done
不打印 waitUntilAllOperationsAreFinished
All Tasks Done
Begin Task 0
Begin Task 1
Begin Task 4
Begin Task 3
Begin Task 5
Finish Task 0
Begin Task 2
deinit 0
Finish Task 1
deinit 1
Finish Task 2
deinit 2
Finish Task 3
deinit 3
Finish Task 4
deinit 4
Finish Task 5
deinit 5
自定义操作。
class CustomOperation: Operation {
public enum State {
case ready
case running
case finished
}
private var state: State = .ready
override var isAsynchronous: Bool { return true }
override open var isExecuting: Bool { state == .running }
override open var isFinished: Bool { state == .finished }
var value: Int = 0
init(value: Int) {
super.init()
self.value = value
}
override func main() {
print("Begin Task \(value)")
DispatchQueue.global().asyncAfter(deadline: .now()+DispatchTimeInterval.seconds(value)) {
print("Finish Task \(self.value)")
self.finish()
}
}
func finish() {
willChangeValue(forKey: "isExecuting")
willChangeValue(forKey: "isFinished")
state = .finished
didChangeValue(forKey: "isFinished")
didChangeValue(forKey: "isExecuting")
}
deinit {
print("deinit")
}
}
比等待更好的方法是例如观察操作计数
import Combine
DispatchQueue.global().async {
(0...5).forEach {
queue.addOperation(CustomOperation(value: [=10=]))
}
}
var store : AnyCancellable?
store = queue.publisher(for: \.operationCount)
.sink { value in
if value == 0 { print("All Tasks Done")}
}
我建议用Combine,传统KVO也可以
不知道这种行为,但我认为您不能在这里做某事。
如果您担心操作顺序,您可以将队列的 maxConcurrentOperationCount
设置为 1,这样您就可以保持顺序。
如果您关心内存并且您有一些巨大的数据,您可以在 finish()
方法中摆脱它或使用 completionBlock
来传递它。
还有在 OperationQueue
属性上使用 KVO 的选项,它的大部分属性都符合 KVO 和 KVC 标准,您可以对其中一些设置观察以触发回调。
如果您正在部署目标 >=13,您可以使用 vadian 已经编写的 Combine。