缓慢 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。
下面是我现有的查询下载和 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。