简体   繁体   English

iOS Swift CoreData图像数据的异步下载

[英]iOS Swift CoreData Asynchronous Downloading of Image Data

As someone who is quite new to Swift and CoreData, I am sure I am going about this the wrong way and am hoping someone might be able to help. 作为Swift和CoreData的新手,我确信我会以错误的方式进行尝试,并希望有人能够提供帮助。

Background: I am downloading JSON over an API, and caching the content locally on an iPad in CoreData. 背景:我正在通过API下载JSON,然后在iPad上的CoreData中本地缓存内容。 As part of the process, I need to download a small image thumbnail, which I am also storing in CoreData (as a Transformable ). 在此过程中,我需要下载一个小图像缩略图,并将其存储在CoreData中(作为Transformable )。

I was doing: My original implementation downloaded images and saved them to CoreData, but although it is being triggered from a background thread (a callback following the API call), the actual downloading of the images seems to cause the app to hang: 我正在做:我的原始实现下载了图像并将其保存到CoreData,但是尽管它是从后台线程(API调用后的回调)触发的,但是图像的实际下载似乎导致应用程序挂起:

let data = NSData(contentsOfURL: imgURL)
if let imageData = data {
    coreDataEntity.thumbnail = imageData
}

What I am now doing: I have since updated my code to the following: 我现在正在做什么:从那以后,我将代码更新为以下内容:

func downloadImage(url: NSURL, cdEntity: CoreDataEntity, moc: NSManagedObjectContext, handler: ((image: UIImage?, cdEntity: CoreDataEntity, moc: NSManagedObjectContext, NSError!) -> Void))
{
    var imageRequest: NSURLRequest = NSURLRequest(URL: url)
    NSURLConnection.sendAsynchronousRequest(imageRequest,
        queue: NSOperationQueue.mainQueue(),
        completionHandler: {response, data, error in
             handler(image: UIImage(data: data), cdEntity: cdEntity, moc: moc, error)
    })
}

func setImage(image: UIImage?, cdEntity: CoreDataEntity, moc: NSManagedObjectContext, error: NSError?) {
    cdEntity.thumbnail = image
    save(moc)
}

I then call the code thus: 然后,我这样调用代码:

downloadImage(imgURL, coreDataEntity, moc, { (image, cdEntity, moc, error) -> Void in
    setImage(image, cdEntity, moc, error)
})

Doing this, I don't get any blocks on the main thread that I notice. 这样做,我在主线程上没有发现任何块。 Inspecting the sqlite file from the device, it looks as though the data is getting set properly, but the images are not showing up in the UI. 从设备检查sqlite文件,看起来好像数据已正确设置,但是图像未显示在UI中。

Is my method completely wrong? 我的方法完全错误吗? If not, how can I inform the UI that the core data model has been updated in such a way that will cause a refresh? 如果不是,我如何通知UI核心数据模型已经过更新,将导致刷新? What function can be called to update a specific row/cell of a table/uicollectionview? 可以调用什么函数来更新表/ uicollectionview的特定行/单元格?

  1. Your NSURLConnection's callbacks are happening on the main queue. 您的NSURLConnection的回调发生在主队列上。 This is one reason your application is locking up. 这是您的应用程序被锁定的原因之一。

  2. Your managed object context, at least in the code you have posted, is not following the Core Data concurrency rules. 您的托管对象上下文(至少在您发布的代码中)没有遵循Core Data并发规则。 You have a choice between thread confinement (unfortunately, the default, which is obsolete), and queue confinement. 您可以在线程限制(不幸的是,默认值(已过时))和队列限制之间进行选择。

With thread confinement, you can only use a managed object context from the thread where it was created. 通过线程限制,您只能使用创建对象的线程中的托管对象上下文。 If you created your managed object context on the main thread (or queue), you can only use it from there - so any Core Data operation you perform on that context will block the main thread, and in turn the UI. 如果在主线程(或队列)上创建了托管对象上下文,则只能从那里使用它-因此,您在该上下文上执行的任何Core Data操作都将阻止主线程,进而阻止UI。

Using queue confinement (almost) any operation on the managed object context must go through either the performBlock: or performBlockAndWait: methods. 使用队列限制(几乎),在托管对象上下文上的任何操作都必须通过performBlock:performBlockAndWait:方法。 The enqueued block is executed on the context's serial queue. 排队的块在上下文的串行队列上执行。 This is far less failure prone than thread confinement and has been the recommended practice for concurrency since iOS 5. 自iOS 5以来,这比线程限制要容易发生的故障少,并且是并发的推荐做法。

If not, how can I inform the UI that the core data model has been updated in such a way that will cause a refresh? 如果不是,我如何通知UI核心数据模型已经过更新,将导致刷新? What function can be called to update a specific row/cell of a table/uicollectionview? 可以调用什么函数来更新表/ uicollectionview的特定行/单元格?

Typically this would be done using an NSFetchedResultsController , which observes a managed object context for changes relevant to it's fetch request. 通常,这可以使用NSFetchedResultsController来完成,该NSFetchedResultsController观察托管对象上下文中与获取请求有关的更改。 Once the initial fetch populates the controller it will listen for changes to the context that affect the objects specified by the fetch request. 初始提取操作填充到控制器后,它将监听对上下文的更改,这些更改会影响获取请求所指定的对象。 When relevant changes occur the controller informs it's delegate through callbacks. 当发生相关更改时,控制器通过回调通知其委托。

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

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