I have an entity named Image with a binary attribute named imageData. I need to cycle trough all Image objects and do something with the data contained inside the imageData attribute.
This is the code:
for image in managedObjectContext.allImages {
autoreleasepool {
var imageData = image.imageData
}
}
I have a lot of objects and every imageData has something like 500KB of data for a total of 3GB of data.
The problem is that each time the imageData attribute is used, the data go into memory and not released until the loop is done. This is causing the memory to grow up to 3GB crashing the app.
I also tried to turn the object into a fault when I'm done with it by calling refresh(_:mergeChanges:) but nothing changes:
for image in managedObjectContext.allImages {
autoreleasepool {
var imageData = image.imageData
managedObjectContext.refresh(image, mergeChanges: false)
}
}
How can I cycle trough all objects without filling up all the memory?
Thank you
I found that a way to reclaim memory is to frequently reset the context. Since objects are invalidated after a reset, I decided to work with objects ids instead (these will remain the same after a reset).
This is my new code:
var objectsIds = managedObjectContext.allImages.map({$0.objectID})
for objectsIdsChunk in objectsIds.chunked(into: 100) {
for objectId in objectsIdsChunk {
autoreleasepool {
if let image = managedObjectContext.object(with: objectId) as? Image {
var imageData = image.imageData
}
}
}
managedObjectContext.reset()
}
This is the array chunked extension:
extension Array {
func chunked(into size: Int) -> [[Element]] {
return stride(from: 0, to: count, by: size).map {
Array(self[$0 ..< Swift.min($0 + size, count)])
}
}
}
With this solution the memory level remain low.