我有一个类Complicated
,其中(它不是真实代码(:
class BeReadyInSomeTime {
var someData: SomeData
var whenDone: () -> Void
var isDone: Bool = false
var highRes: [LongCountedStuff] = []
init(data:SomeData, whenDone: @escaping () - >Void) {
self.someData = someData
self.whenDone = whenDone
... prepare `highRes` in background...
{ makeHighRes() }
... and when done set `isDone` to `true`, fire `whenDone()`
}
func reset(data:SomeData) {
self.someData = someData
self.isDone = false
self.highRes = []
... forget **immediately** about job from init or reset, start again
{ makeHighRes() }
... and when done set `isDone` to `true`, fire `whenDone()`
}
var highResolution:AnotherType {
if isDone {
return AnotherType(from: highRes)
} else {
return AnotherType(from: someData)
}
}
func makeHighRes() {
var result = [LongCountedStuff]
// prepare data, fast
let some intermediateResult = almost ()
self.highRes = result
}
func almost() -> [LongCountedStuff] {
if isNice {
return countStuff(self.someData)
} else {
return []
}
func countStuff(stuff:[LongCountedStuff], deep:Int = 0) -> [LongCountedSuff] {
if deep == deep enough {
return stuff
} else {
let newStuff = stuff.work
count(newStuff, deep: deep+1)
}
}
制作highRes
数组是一个递归函数,它会多次调用自己,有时需要几秒钟的时间,但我需要尽可能快的反馈(它将是someData
元素之一,所以我很安全(。据我所知,我只能"标记"已取消的DispatchWorkItem
。如果我每秒通过reset
传递几次新数据(通过鼠标拖动(,那么整个块在后台的计数次数与传递数据的次数相同。如何处理这种问题?真的要打破计数highRes
?
如果您有一个例程不断调用另一个框架,并且您想在一次迭代结束时和下一次迭代开始前停止它,那么将其封装在Operation
中并检查isCancelled
是一个好模式。(你也可以使用GCD和DispatchWorkItem
,也可以使用它的isCancelled
,但我发现操作更优雅。(
但是,如果你说你不仅想取消循环,还希望停止该框架内的消费调用,那么,不,你不能这样做(除非框架提供了自己的取消机制(。但没有先发制人的取消。除非在计算中添加检查以查看是否已取消,否则不能仅停止耗时的计算。
我还要问递归模式是否正确。你真的需要一次计算的结果才能开始下一次吗?如果是这样,那么递归(或迭代(模式就可以了。但是,如果递归操作只是为了传递下一个工作单元,那么非递归模式可能会更好,因为它打开了并行计算的可能性。
例如,您可以创建一个具有某个合理值(例如4或6(的maxConcurrencyCount
的并发队列。然后将每个单独的处理任务封装在其自己的Operation
子类中,并让每个检查其各自的isCancelled
。然后,您可以将所有操作添加到前面,然后让队列从那里处理它。当您想阻止它们时,可以将队列告知cancelAllOperations
。这是一个相对简单的模式,允许您并行进行计算,并且是可取消的。但这显然只有在给定的运算不严格依赖于先前运算的结果的情况下才有效。