上周,我向一位 Apple 工程师发送了一封电子邮件,告知我的NSMetadataQuery
.
这是电子邮件:
你好,
我正在编写一个基于文档的应用程序或 iOS,我的重命名方法(将文档移动到新位置)似乎与正在运行的 NSMetadataQuery 冲突。
调用 move 方法后查询会更新几次,第一次是刚刚移动的项目的旧 URL,下一次是新 URL。但是,由于我的更新方法(如下),如果自更新以来已删除 URL,我的模型将删除已删除的 URL,反之亦然,如果它发现 URL 尚不存在。
我认为我的问题是两个问题之一,要么是 NSMetadataQuery 的更新方法不足,并且在删除之前不检查项目的 URL 是否有“正确”属性(尽管查看文档我看不到任何暗示我的东西)我错过了一些东西)或者我的重命名方法没有做它应该做的事情。
我尝试在重命名方法开始时禁用更新,并在所有完成块完成后重新启用,但这没有任何区别。
我的 NSMetadataQuery 的更新方法:
func metadataQueryDidUpdate(notification: NSNotification) {
ubiquitousItemsQuery?.disableUpdates()
var ubiquitousItemURLs = [NSURL]()
if ubiquitousItemsQuery != nil && UbiquityManager.sharedInstance.ubiquityIsAvailable {
for var i = 0; i < ubiquitousItemsQuery?.resultCount; i++ {
if let result = ubiquitousItemsQuery?.resultAtIndex(i) as? NSMetadataItem {
if let itemURLValue = result.valueForAttribute(NSMetadataItemURLKey) as? NSURL {
ubiquitousItemURLs.append(itemURLValue)
}
}
}
// Remove deleted items
//
for (index, fileRepresentation) in enumerate(fileRepresentations) {
if fileRepresentation.fileURL != nil && !contains(ubiquitousItemURLs, fileRepresentation.fileURL!) {
removeFileRepresentations([fileRepresentation], fromDisk: false)
}
}
// Load documents
//
for (index, fileURL) in enumerate(ubiquitousItemURLs) {
loadDocumentAtFileURL(fileURL, completionHandler: nil)
}
ubiquitousItemsQuery?.enableUpdates()
}
}
我的重命名方法:
func renameFileRepresentation(fileRepresentation: FileRepresentation, toNewNameWithoutExtension newName: String) {
if fileRepresentation.name == newName || fileRepresentation.fileURL == nil || newName.isEmpty {
return
}
let newNameWithExtension = newName.stringByAppendingPathExtension(NotableDocumentExtension)!
// Update file representation
//
fileRepresentation.nameWithExtension = newNameWithExtension
if let indexPath = self.indexPathForFileRepresentation(fileRepresentation) {
self.reloadFileRepresentationsAtIndexPaths([indexPath])
}
UbiquityManager.automaticDocumentsDirectoryURLWithCompletionHandler { (documentsDirectoryURL) -> Void in
let sourceURL = fileRepresentation.fileURL!
let destinationURL = documentsDirectoryURL.URLByAppendingPathComponent(newNameWithExtension)
// Update file representation
//
fileRepresentation.fileURL = destinationURL
if let indexPath = self.indexPathForFileRepresentation(fileRepresentation) {
self.reloadFileRepresentationsAtIndexPaths([indexPath])
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { () -> Void in
let coordinator = NSFileCoordinator(filePresenter: nil)
var coordinatorError: NSError?
coordinator.coordinateWritingItemAtURL(sourceURL, options: .ForMoving, writingItemAtURL: destinationURL, options: .ForReplacing, error: &coordinatorError, byAccessor: { (newSourceURL, newDestinationURL) -> Void in
var moveError: NSError?
let moveSuccess = NSFileManager().moveItemAtURL(newSourceURL, toURL: newDestinationURL, error: &moveError)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
assert(moveError == nil || moveSuccess, "Error renaming (moving) document from \(newSourceURL) to \(newDestinationURL).\nSuccess? \(moveSuccess).\nError message: \(moveError).")
if let query = self.ubiquitousItemsQuery {
query.enableUpdates()
}
if moveError != nil || moveSuccess {
// TODO: Implement resetting file rep
}
})
})
})
}
}
我几乎立刻就得到了答复,但从那以后就没有任何答复。
这是回复
让我印象深刻的一件大事是您对 disableUpdates() 和 enableUpdates() 的使用。您在运行循环的同一轮中执行它们,但 NSMetadataQuery 异步提供结果。由于此代码在您的更新通知中执行,因此它与查询同步执行。因此,从查询的角度来看,它将通过发布通知开始传递更新。发布通知是一个同步过程,因此在发布通知时,更新将被禁用并重新启用。因此,当查询完成发布通知时,它会回到与开始交付结果时完全相同的状态。听起来这不是您想要的行为。
这是我需要帮助的地方
我假设 NSMetadataQuery 有某种缓存,当更新被禁用时它会添加结果,当启用时,那些(可能很多)缓存结果会循环通过,每个缓存结果都通过更新通知发送。
无论如何,我查看了 iOS 上的运行循环,虽然我自己尽可能多地了解它们,但我不明白回复有什么帮助,即如何实际解决问题 - 或者是什么导致了问题.
如果有人有任何好主意,我很乐意为您提供帮助!
谢谢。
更新
这是我的函数何时开始和结束的日志:
start metadataQueryDidUpdate:
end metadataQueryDidUpdate:
start metadataQueryDidUpdate:
end metadataQueryDidUpdate:
start renameFileRepresentation:toNewNameWithoutExtension
start metadataQueryDidUpdate:
end metadataQueryDidUpdate:
end renameFileRepresentation:toNewNameWithoutExtension
start metadataQueryDidUpdate:
end metadataQueryDidUpdate:
start metadataQueryDidUpdate:
end metadataQueryDidUpdate:
start metadataQueryDidUpdate:
end metadataQueryDidUpdate: