0

上周,我向一位 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:
4

1 回答 1

0

我遇到了同样的问题。NSMetaDataQuery 更新会告诉您是否有更改,但不会告诉您更改是什么。如果更改是重命名,则无法识别以前的名称,因此我可以在我的 tableView 中找到旧条目。非常令人沮丧。

但是,您可以通过使用 NSFileCoordinator 和 NSFilePresenter 来获取信息。

使用 NSFilePresenter 方法presentSubitemAtURL(oldURL: NSURL, didMoveToURL newURL: NSURL)

如您所述,查询更改通知会使用旧 URL 调用一次,然后使用新 URL 调用一次。上面的方法在这两个通知之间调用。

于 2014-10-26T18:43:42.443 回答