缓慢 CloudKit table 滚动 - 更改现有代码?

Slow CloudKit table scrolling - altering existing code?

下面是我现有的查询下载和 table 行代码的单元格...

 publicDB.perform(query, inZoneWith: nil)
    {
        (results, error) -> Void in
        if (error != nil)
        {
            self.present(alert, animated: true, completion: nil)
        }
        else
        {
            for result in results!
            {
                self.restaurantArray.append(result)
            }
            OperationQueue.main.addOperation( { () -> Void in
                self.tableView.reloadData()
            }) } }}
downloadRestaurants()
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "restaurantcell") as? RestaurantTableCell
    let restaurant: CKRecord = restaurantArray[indexPath.row]
    cell?.name?.text = restaurant.value(forKey: "Name") as? String
    let asset = restaurant.value(forKey: "Picture") as! CKAsset 
    let data = try! Data(contentsOf: asset.fileURL)
   _ = UIImage(data: data)
    cell?.picture?.image = UIImage(data: data)
   return cell!
}

当我 运行 这段代码时,应用程序仍然可以正常运行,但滚动浏览 10 个左右的 table 单元格非常不稳定。我不确定是什么原因造成的——所有记录,每条记录都包含一个图像,是在 top 函数的查询下载部分下载的。但是,我遗漏的问题或概念在 运行 期间一直存在。我在这里错过了什么?延迟加载?缓存?还有什么?目前不确定,所以任何帮助都会非常有帮助。

更新 1:

我已经更新了我的代码,非常感谢 Pierce。我不得不根据他的回答稍微更新我的代码,以维护一个 ckrecord 数组以通过 - restaurantArray 转到另一个控制器,但也为 NSObject class - tablerestaurantarray 创建一个新数组到显示在当前 table 控制器中。

   var restaurantArray: Array<CKRecord> = []
    var tablerestaurantarray: [Restaurant] = []

for result in results!
            {
                let tablerestaurant = Restaurant()

                if let name = result.value(forKey: "Name") as! String? {
                    tablerestaurant.name = name
                }
                // Do same for image
                if let imageAsset = result.object(forKey: "Picture") as! CKAsset? {

                    if let data = try? Data(contentsOf: imageAsset.fileURL) {
                        tablerestaurant.image =  UIImage(data: data)
                    }
                }
                self.tablerestaurantarray.append(tablerestaurant)

                self.restaurantArray.append(result)
            }
            OperationQueue.main.addOperation( { () -> Void in
                self.tableView.reloadData()     
            })
        }
    }
}
downloadRestaurants() 
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return restaurantArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "restaurantcell") as? RestaurantTableCell

    let restaurant: Restaurant = tablerestaurantarray[indexPath.row]
    cell?.name?.text = restaurant.name

    cell?.picture?.image = restaurant.image
    return cell!
}

您的代码设置方式,每当您在 UITableView 中滚动时,您的程序都会将 CKAsset 转换为 Data,然后将其转换为 UIImage,它在每个细胞中!这是一个相当低效的过程,所以尝试创建一个名为 Restaurant 的 NSObject,它有一个图像 属性,当你遍历从 CKQuery 返回的所有记录时,将每条记录解析为一个新的 Restaurant 对象。要创建一个新的 NSObject,转到文件 -> 新建 -> 文件 -> select 'Swift File' 并添加如下内容:

import UIKit

class Restaurant: NSObject {

    // Create a UIImage property
    var image: UIImage?

    // Add any other properties, i.e. name, address, etc.
    var name: String = ""
}

现在您的查询:

// Create an empty array of Restaurant objects
var restaurantArray: [Restaurant] = []

publicDB.perform(query, inZoneWith: nil) { (results, error) -> Void in
    if (error != nil) {
        self.present(alert, animated: true, completion: nil)
    } else {
        for result in results! {

            // Create a new instance of Restaurant
            let restaurant = Restaurant()

            // Use optional binding to check if value exists
            if let name = result.value(forKey: "Name") as! String? {
                restaurant.name = name
            }
            // Do same for image
            if let imageAsset = result.object(forKey: "Picture") as! CKAsset? {

                if let data = try? Data(contentsOf: imageAsset.fileURL) {
                    restaurant.image =  UIImage(data: data)
                }
            }

            // Append the new Restaurant to the Restaurants array (which is now an array of Restaurant objects, NOT CKRecords)
            self.restaurantArray.append(restaurant)
        }
        OperationQueue.main.addOperation( { () -> Void in
            self.tableView.reloadData()
        }) 
    } 
}

现在您的手机设置简单多了:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCell(withIdentifier: "restaurantcell") as? RestaurantTableCell

    let restaurant: Restaurant = restaurantArray[indexPath.row]
    cell?.name?.text = restaurant.name

    cell?.picture?.image = restaurant.image
    return cell!
}

您应该使用 CKQueryOperation 来为您的 UITableView 实现分页。

您必须将 resultLimit 属性 设置为等于您一次可见的细胞数量 table 加上 3 或 4

设置 recordFetchedBlock 属性 您必须在其中实施将应用于一个 CKRecord

的代码

设置queryCompletionBlock属性。这是分页代码中最重要的部分,因为此闭包接收一个可选的 CKQueryCursor 参数。

如果此 CKQueryCursor 为 nil,则您已到达可用于查询的最后一条记录,但如果它不是 nil 值,则使用此 CKQueryCursor 作为下一次提取的指示符,您还有更多记录要提取。

当用户在您的 TableView 上滚动并到达最后一个元素时,您应该使用 CKQueryCursor 执行另一次获取。

其他性能建议是应该在单独的执行队列上处理 CKAssets。