UIImage causes memory to run out

I have a project that currently has data saved locally and I'm trying to get it to sync over multiple devices.

Currently basic data is syncing perfectly fine, but I'm having issues getting the images to convert to data. From what I've researched it because I'm using a UIImage to convert and this caches the image

It works fine when there's only a few images, but if there's several its a pain

The associated code

    func updateLocalImages() {
        autoreleasepool {
            let fetchRequest: NSFetchRequest<Project> = Project.fetchRequest()
            fetchRequest.predicate = NSPredicate(format: "converted = %d", false)
            fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \Project.statusOrder?.sortOrder, ascending: true), NSSortDescriptor(keyPath: \Project.name, ascending: true)]
            
            do {
                let projects = try viewContext.fetch(fetchRequest)
                for project in projects {
                    currentPicNumber = 0
                    currentProjectName = project.name ?? "Error loading project"
                    if let pictures = project.pictures {
                        projectPicNumber = pictures.count
                        for pic in pictures {
                            currentPicNumber = currentPicNumber + 1
                            let picture : Picture = pic as! Picture
                            if let imgData = convertImage(picture: picture) {
                                picture.pictureData = imgData
                            }
                        }
                        project.converted = true
                        saveContext()
                    }
                }
            } catch {
                print("Fetch Failed")
            }
        }
    }
    
    func convertImage(picture : Picture)-> Data? {
        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        let path = paths[0]
        if let picName = picture.pictureName {
            let imagePath = path.appendingPathComponent(picName)
            if let uiImage = UIImage(contentsOfFile: imagePath.path) {
                if let imageData = uiImage.jpegData(compressionQuality: 0.5) {
                    return imageData
                }
            }
        }
        return nil
    }```

Answered by DTS Engineer in 823235022

I can resolve it by refreshing the context after each project save, but not sure if its the best way of doing it.

It's great that the issue has been solved. Just to add a bit from the Core Data perspective to hopefully make things clear: When your app fetches or creates objects in a managed context, Core Data caches the objects to avoid a round trip to the store file when the app uses those objects again, which grows the memory footprint of an app as it processes more and more objects.

You can lower the memory footprint by calling reset().

Note that if your app retains a managed object associated with a context, resetting the context will invalidate the object, and so you will need to validate the object after the reset by refetching the object or calling object(with:)).

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Try this:

NSData* data = [NSData dataWithContentsOfURL:url options:NSDataReadingMappedIfSafe error:&error];

This will create a mapped region of memory instead of a private allocation.

Rico

WWDR - DTS - Software Engineer

After a bit of digging it appears the issue isn't related to to conversion itself. It's occurring when writing the data to core data.

I can resolve it by refreshing the context after each project save, but not sure if its the best way of doing it.

I can resolve it by refreshing the context after each project save, but not sure if its the best way of doing it.

It's great that the issue has been solved. Just to add a bit from the Core Data perspective to hopefully make things clear: When your app fetches or creates objects in a managed context, Core Data caches the objects to avoid a round trip to the store file when the app uses those objects again, which grows the memory footprint of an app as it processes more and more objects.

You can lower the memory footprint by calling reset().

Note that if your app retains a managed object associated with a context, resetting the context will invalidate the object, and so you will need to validate the object after the reset by refetching the object or calling object(with:)).

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

UIImage causes memory to run out
 
 
Q