【问题标题】:NSMetadataQuery’s update notification interferes with (run loop?)NSMetadataQuery 的更新通知干扰(运行循环?)
【发布时间】:2014-12-12 06:33:31
【问题描述】:

上周我向 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:

【问题讨论】:

    标签: ios notifications nsrunloop nsmetadataquery


    【解决方案1】:

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

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

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

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

    【讨论】:

      猜你喜欢
      • 2019-03-20
      • 1970-01-01
      • 2016-04-10
      • 2014-06-21
      • 2020-08-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多