简体   繁体   English

从CloudKit获取CKAsset图像非常慢

[英]Fetching CKAsset Image From CloudKit is Very Slow

I am using CloudKit as a server backend for my iOS application. 我正在使用CloudKit作为我的iOS应用程序的服务器后端。 I'm using it to house some relatively static data along with a handful of images(CKAsset). 我用它来存放一些相对静态的数据以及一些图像(CKAsset)。 I ran into a problem when the time came for me to actually fetch those assets from the public database. 当我花时间从公共数据库中实际获取这些资产时,我遇到了一个问题。 They load at an excruciatingly slow speed. 它们以极慢的速度加载。

My use case is to load an image into every cell inside of a collection view. 我的用例是将图像加载到集合视图内的每个单元格中。 The images are only 200kb in size, but the fetch process took an average of 2.2 seconds for the download to complete and set the image in a cell. 图像的大小仅为200kb,但获取过程平均需要2.2秒才能完成下载并在单元格中设置图像。 For comparison, I took URLs of similar sized stock images and loaded them in using NSURLSession. 为了比较,我使用类似大小的库存图像的URL并使用NSURLSession加载它们。 It took a mere 0.18 - 0.25 seconds for each image to load. 每个图像加载仅需0.18 - 0.25秒。

I have tried multiple different ways of downloading the images from CK: direct fetch of the record, query, and operation query. 我尝试了多种不同的方式从CK下载图像:直接获取记录,查询和操作查询。 All of them have similar results. 所有这些都有类似的结果。 I am also dispatching back to the main queue within the completion block prior to setting the image for the cell. 在为单元格设置图像之前,我还将调度回完成块中的主队列。

My database is setup to have a primary object with several fields of data. 我的数据库设置为具有包含多个数据字段的主对象。 I then setup a backwards reference style system for the photos, where each photo just has a reference to a primary object. 然后我为照片设置了一个向后的参考样式系统,其中每张照片只有一个主要对象的引用。 That way I can load the photos on demand without bogging down the main data. 这样我就可以按需加载照片而不会影响主要数据。

It looks something like this: 它看起来像这样:

Primary Object: title: String, startDate: Date 主要对象: title: String, startDate: Date

Photo Object: owner: String(reference to primary object), image: Asset Photo对象: owner: String(reference to primary object), image: Asset

Here is an example request that I tried to directly fetch one of the photos: 以下是我尝试直接获取其中一张照片的示例请求:

let publicDb = CKContainer.defaultContainer().publicCloudDatabase
let configRecordId = CKRecordID(recordName: "e783f542-ec0f-46j4-9e99-b3e3ez505adf")

publicDb.fetchRecordWithID(configRecordId) { (record, error) -> Void in
    dispatch_async(dispatch_get_main_queue()) {
        guard let photoRecord = record else { return }
        guard let asset = photoRecord["image"] as? CKAsset else { return }

        guard let photo = NSData(contentsOfURL: asset.fileURL) else { return }

        let image = UIImage(data: photo)!

        cell.cardImageView.image = image
    }
}

I can't seem to figure out why these image downloads are taking so long, but it's really quite the showstopper if I can't get them to load in a reasonable about of time. 我似乎无法弄清楚为什么这些图像下载需要这么长时间,但如果我无法让它们在合理的时间内加载,那真的是非常明显的。

Update: I tried the fetch operation with a smaller image, 23kb. 更新:我尝试使用较小的图像23kb进行获取操作。 The fetch was faster, anywhere from 0.3 - 1.1 seconds. 获取速度更快,从0.3到1.1秒不等。 That's better, but still doesn't meet the expectation that I had for what CloudKit should be able to provide. 这更好,但仍然不符合我对CloudKit应该提供的期望。

I am using CKQueryOperation. 我正在使用CKQueryOperation。 I found that once I added the following line to my code that downloading CKAssets sped up by about a factor of 5-10x. 我发现,一旦我将以下代码添加到我的代码中,下载CKAssets的速度提高了大约5-10倍。

    queryOperation.qualityOfService = .UserInteractive

Here is my full code: 这是我的完整代码:

func getReportPhotos(report:Report, completionHandler: (report:Report?, error:NSError?) -> ()) {
    let photo : Photo = report.photos![0] as! Photo
    let predicate : NSPredicate = NSPredicate(format: "recordID = %@", CKRecordID(recordName: photo.identifier!))
    let query : CKQuery = CKQuery(recordType: "Photo", predicate: predicate)
    let queryOperation : CKQueryOperation = CKQueryOperation()
    queryOperation.query = query
    queryOperation.resultsLimit = numberOfReportsPerQuery        
    queryOperation.qualityOfService = .UserInteractive
    queryOperation.recordFetchedBlock = { record in
        photo.date = record.objectForKey("date") as? NSDate
        photo.fileType = record.objectForKey("fileType") as? String
        let asset : CKAsset? = record.objectForKey("image") as? CKAsset
        if asset != nil {
            let photoData : NSData? = NSData(contentsOfURL:asset!.fileURL)
            let photo : Photo = report.photos![0] as! Photo
            photo.image = UIImage(data:photoData!)
        }

    }
    queryOperation.queryCompletionBlock = { queryCursor, error in
        dispatch_async(dispatch_get_main_queue(), {
            completionHandler(report: report, error: error)
        })
    }
    publicDatabase?.addOperation(queryOperation)
}

There seems to be something slowing down your main thread which introduces a delay in executing the capture block of your dispatch_async call. 似乎有一些东西减慢了你的主线程,这导致执行dispatch_async调用的捕获块的延迟。 Is it possible that your code calls this record fetching function multiple times in parallel ? 您的代码是否可能多次并行调用此记录获取函数? This would cause the NSData(contentsOfURL: asset.fileURL) processing to hog the main thread and introduce cumulative delays. 这将导致NSData(contentsOfURL:asset.fileURL)处理占用主线程并引入累积延迟。

In any case, if only as a good practice, loading the image with NSData should be performed in the background and not on the main thread. 在任何情况下,如果仅作为一种好的做法,使用NSData加载图像应该在后台执行而不是在主线程上执行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM