ergoon
ergoon

Reputation: 1264

CoreData memory not released after importing data

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:

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

Answers (1)

Wain
Wain

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

Related Questions