我有一些NSOperations
异步创建一些数据。我想将所有结果收集到一个数组中。因为我在多个不同的线程上访问数组,所以我已经锁定了数组。
NSOperationQueue
将数据附加到数组,但结果似乎错过了一些数据对象。每次运行时结果似乎都会改变。
我创建了一个简化的示例项目,可以重新创建问题。代码在Swift中,但我不认为这是特定于Swift的。
import UIKit
class ViewController: UIViewController {
let queue = NSOperationQueue()
var bucket = [String]()
override func viewDidLoad() {
super.viewDidLoad()
queue.addObserver(self, forKeyPath: "operations", options: NSKeyValueObservingOptions.New, context: nil)
for _ in 0..<10 {
queue.addOperation(NSBlockOperation {
// Let's pretend that creating the "fish" string is actually potentially
// expensive and that's why we're doing it in an NSOperation.
let fish = "fish"
objc_sync_enter(self.bucket)
self.bucket.append(fish)
let fishCount = self.bucket.count
print("Bucket contains \(fishCount) fish" + ((fishCount != 1) ? "es" : ""))
objc_sync_exit(self.bucket)
})
}
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if let keyPath = keyPath {
if let object = object as? NSOperationQueue {
if object == queue && keyPath == "operations" {
if queue.operationCount == 0 {
objc_sync_enter(self.bucket)
let fishCount = bucket.count
print("Bucket finally contains \(fishCount) fish" + ((fishCount != 1) ? "es" : ""))
objc_sync_exit(self.bucket)
}
} else {
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
}
}
}
}
结果各不相同,但通常是这样的:
Bucket contains 1 fish
Bucket contains 1 fish
Bucket contains 1 fish
Bucket contains 1 fish
Bucket contains 2 fishes
Bucket contains 1 fish
Bucket contains 1 fish
Bucket contains 3 fishes
此外,有时代码在行EXC_BAD_ACCESS
上的self.bucket.append(fish)
崩溃了
此外,print("Bucket finally contains \(fishCount) fish" + ((fishCount != 1) ? "es" : ""))
中的observeValueForKeyPath
行永远不会被调用。我不确定这是否是一个单独的问题。
答案 0 :(得分:1)
您应该查看子类NSOperation,因为它是一个抽象类。 有关子类化,请参阅此Stackoverflow question。 考虑到这一点,我建议您在每个操作实例上都有一个标识符属性,以便您可以跟踪您的操作,这样您就可以知道所有操作何时完成。您也可以考虑将这些代码从视图控制器类中拉出来并创建一个类来处理您的鱼类它会帮助您在将来不再对鱼类感兴趣时进行封装:)
The Concurrency Programming Guide非常擅长解释异步应用程序设计的基础知识。
NSOperation类是一个抽象类,用于封装与单个任务关联的代码和数据。因为它是抽象的,所以不直接使用此类,而是使用系统定义的子类(NSInvocationOperation或NSBlockOperation)子类或使用其中一个来执行实际任务。尽管是抽象的,但NSOperation的基本实现确实包含了协调安全执行任务的重要逻辑。这种内置逻辑的存在使您可以专注于任务的实际实现,而不是确保它与其他系统对象正常工作所需的粘合代码。