【问题标题】:Core Data in Swift: Comparing objects in a for loopSwift 中的核心数据:在 for 循环中比较对象
【发布时间】:2015-05-22 20:51:42
【问题描述】:

我正在尝试通过检查自上次检查后是否有任何MPMediaItemPropertyPlayCounts 发生更改来更新用户的音乐收听历史记录。现在,每次检查时,我都会查询整个 iPod 库并比较它们在 Core Data 中已有的播放次数。如果当前播放计数 != 存储在 Core Data 中的播放计数,我们会对这首歌做一些事情,因为我们知道用户最近听过它。

在我的代码中,我正在努力循环遍历所有 iPod 歌曲同时搜索相应的 Core Data 对象。下面的代码打印出太多行。如何在 for 循环中搜索 Core Data 中的对象?

class func checkiPodSongsForUpdate() {

        var appDel: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate

        var context: NSManagedObjectContext = appDel.managedObjectContext!

        var newSong = NSEntityDescription.insertNewObjectForEntityForName("IPodSongs", inManagedObjectContext: context) as! NSManagedObject

        var request = NSFetchRequest(entityName: "IPodSongs")

        request.returnsObjectsAsFaults = false

        var results = context.executeFetchRequest(request, error: nil)


        let query = MPMediaQuery.songsQuery()

        let queryResult = query.collections as! [MPMediaItemCollection]

        for song in queryResult {

            for song in song.items as! [MPMediaItem] {

                request.predicate = NSPredicate(format: "title = %@", "\(song.valueForProperty(MPMediaItemPropertyTitle))")

                for result: AnyObject in results! {

                    if let playCount = result.valueForKey("playCount") as? String {

                        if playCount != "\(song.valueForProperty(MPMediaItemPropertyPlayCount))" {

                            println("\(song.valueForProperty(MPMediaItemPropertyTitle)) has a new play count.")

                        } else {

                            println("User hasn't listened to this song since last check.")
                        }
                    }
                }
            }
        }
    }

【问题讨论】:

    标签: ios swift for-loop core-data


    【解决方案1】:

    直接的问题是您为request.predicate 分配了一个值,但这样做没有任何效果。提取请求谓词只有在您进行提取之前分配它 才有效。它成为 fetch 的一部分,并且 fetch 结果只包括与谓词匹配的对象。

    解决这个特定问题有几种可能性:

    1. 在循环内分配谓词之后,在循环中获取核心数据。这将得到您期望的结果。但是,它也会非常效率低下,并且无法很好地扩展。获取请求是相对昂贵的操作,因此您可能会发现自己花费了大量时间来执行这些操作。

    2. 不要给request.predicate赋值,而是通过执行类似的操作过滤内存中的results数组

      let predicate = NSPredicate(format: "title = %@", "\(song.valueForProperty(MPMediaItemPropertyTitle))")
      let currentResults = results.filteredArrayUsingPredicate(predicate)
      

      这将为您提供一个仅包含与谓词匹配的歌曲的数组。这将比选项 #1 更快,但它仍然留下了您在此处获取 所有 歌曲的主要问题。这可能意味着每次调用此函数时都会将大量托管对象加载到内存中(假设用户有 50,000 首歌曲?)。这仍然不是一个好的解决方案。不幸的是,我对MPMediaQuery 了解不多,无法推荐更好的方法。

    其中任何一个都可以解决眼前的问题,但您需要解决更重要的概念问题。您正在比较两个可能较大的集合以寻找单个匹配项。无论您如何处理,这都会占用大量 CPU 时间或内存,或两者兼而有之。

    【讨论】:

    • 感谢汤姆的回答。但是我不能使用NSPredicate 类型的参数列表调用filteredArrayUsingPredicate。您更广泛的想法是将 Core Data 搜索结果存储在字典中(我将为每首歌曲提供标题、艺术家和播放次数),然后使用 MPMediaQuery? 循环遍历该字典?
    • 我不知道您的意思是不能使用 NSPredicate 的参数调用 filteredArrayUsingPredicate。这是调用该方法的唯一方法。不,我没有提到你描述的使用字典,我也不推荐它。我确实建议您尝试找到一个比您尝试比较两个大型对象集合更好的解决方案。
    • 我正在删除 request.predicate 行,并用您的建议替换它,我收到了这个错误。这在 Xcode 中真的对你有用吗?这对我来说当然是有道理的,只是无法弄清楚为什么我会收到这个错误。
    【解决方案2】:

    找到了我自己的解决方案。正如@Tom 所说,如果每个数组中有足够的对象,遍历两个数组可能会变得很昂贵。我决定遍历arrayOne,并在该循环中调用一个自定义函数,在arrayTwo 中查找给定的键/值对。

    // Custom function that returns Bool if arrayTwo contains a given key/value pair from inside the arrayOne loop
    
            func arrayContains(array:[[String:String]], #key: String, #value: String) -> Bool {
                for song in array {
                    if song[key] == value {
                        return true
                    }
                }
                return false
            }
    

    非常感谢@vacawama 在这里提供的帮助:Find key-value pair in an array of dictionaries

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-08-04
      • 1970-01-01
      • 2012-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多