Reputation: 1264
I have an issue with importing data to a coredata project with swift. I'm importing a lot of images, which results in quite a big memory footprint (around 100 - 120MB). The problem is, that once the images are downloaded and imported, I save the managedObjectContext and reset it, but the memory is not released. If however, I send the app to the background, most of the memory is released (I end up with ~50MB).
I use the following CoreData setup:
masterContext
with PrivateQueueConcurrencyType
mainContext
with MainQueueConcurrencyType
and masterContext
as parentContext
PrivateQueueConcurrencyType
and the mainContext
as it's parentContext
the following method starts the import:
func downloadProductImages(completion: (error: NSError?) -> Void) {
if let moc = self.backgroundMOC {
moc.performBlock { () -> Void in
moc.reset()
var err: NSError? = nil
self.importBrandImages({ (error) -> Void in
if error != nil {
completion(error: error)
return
}
moc.save(nil)
self.importProductVariantThumbnails({ (error) -> Void in
if error != nil {
completion(error: error)
return
}
moc.save(nil)
moc.reset()
self.backgroundMOC = nil
completion(error: err)
})
})
}
}
}
and these methods download the images and save them in the database:
private func importBrandImages(completion: (error: NSError?) -> Void) {
if let moc = self.backgroundMOC {
moc.performBlock { () -> Void in
moc.reset()
var error: NSError? = nil
let brandsFetchReq = NSFetchRequest(entityName: "Brand")
// brand images
if let brands = moc.executeFetchRequest(brandsFetchReq, error: &error) as? [Brand] {
let imageQueue = TaskQueue()
autoreleasepool({ () -> () in
for brand in brands {
if let logoSrc = brand.logoSrc {
imageQueue.tasks +=~ { results, next in
ImageLoader.sharedLoader.imageForUrl(logoSrc.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!, completionHandler: { (image, url) -> () in
if image != nil {
brand.logo = UIImageJPEGRepresentation(image, 0.35)
}
next(nil)
})
}
}
if let bgImgSrc = brand.bgImageSrc {
imageQueue.tasks +=~ { results, next in
ImageLoader.sharedLoader.imageForUrl(bgImgSrc.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!, completionHandler: { (image, url) -> () in
if image != nil {
brand.bgImage = UIImageJPEGRepresentation(image, 0.35)
}
next(nil)
})
}
}
}
})
imageQueue.run(completion: { () -> Void in
moc.save(nil)
completion(error: error)
})
} else {
completion(error: error)
}
}
}
}
private func importProductVariantThumbnails(completion: (error: NSError?) -> Void) {
var err: NSError? = nil
if let moc = self.backgroundMOC {
moc.performBlock { () -> Void in
moc.reset()
let pVariantsFetchReq = NSFetchRequest(entityName: "ProductVariant")
if let variants = moc.executeFetchRequest(pVariantsFetchReq, error: &err) as? [ProductVariant] {
let importQueue = TaskQueue()
autoreleasepool({ () -> () in
for variant in variants {
if let thumbnailSrc = variant.thumbnailSrc {
importQueue.tasks +=~ { results, next in
ImageLoader.sharedLoader.imageForUrl(thumbnailSrc.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!, completionHandler: { (image, url) -> () in
if image != nil {
variant.thumbnail = UIImageJPEGRepresentation(image, 0.35)
}
next(nil)
})
}
}
}
})
importQueue.run { () -> Void in
moc.save(nil)
self.importProductVariantImages({ (error) -> Void in
completion(error: error)
})
}
}
}
}
}
private func importProductVariantImages(completion: (error: NSError?) -> Void) {
var error: NSError? = nil
if let moc = self.backgroundMOC {
moc.performBlock { () -> Void in
moc.reset()
let pImagesFetchReq = NSFetchRequest(entityName: "ProductImage")
// product images
if let images = moc.executeFetchRequest(pImagesFetchReq, error: &error) as? [ProductImage] {
let importQueue = TaskQueue()
autoreleasepool({ () -> () in
for pImage in images {
if let imageSrc = pImage.imageSrc {
importQueue.tasks +=~ { results, next in
ImageLoader.sharedLoader.imageForUrl(imageSrc.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!, completionHandler: { (image, url) -> () in
if image != nil {
pImage.imageData = UIImageJPEGRepresentation(image, 0.35)
}
next(nil)
})
}
}
}
})
importQueue.run(completion: { () -> Void in
moc.save(nil)
completion(error: error)
})
} else {
completion(error: error)
}
}
}
}
I have no idea, why the memory is not released. I used Instruments to find memory leaks, but it doesn't show any.
Upvotes: 0
Views: 368
Reputation: 119021
Your problem appears to be that ImageLoader
contains a cache and is adding all of the images to it. If you don't need that functionality, and it doesn't really look like you do, then you should delete it and simplify the ImageLoader
so it just downloads and returns the images.
Upvotes: 1