I'm iterating over a large collection of photos, since they're too large to fit all of them in memory I have to update them by: 1) loading them from file individually 2) performing the update 3) saving to file. Here's a simplified example (doesn't work in Simulator because of the CIImage
part for some reason):
import SwiftUI
struct Photo {
let body: Data?
let size = CGSize(width: 1000, height: 1000)
let bytesPerPixel: Int = 4
func load() -> Photo {
let data = Data(count: Int(size.width) * Int(size.height) * bytesPerPixel)
return Photo(body: data)
}
}
struct ContentView: View {
let photos: [Photo] = Array(repeating: Photo(body: nil), count: 100)
var body: some View {
VStack {
Button(action: { self.overloadMemory() }) { Text("Overload Memory") }
}
}
private func overloadMemory() {
for photo in photos {
guard let data = photo.load().body else { continue }
let bytesPerRow = Int(photo.size.width) * photo.bytesPerPixel
let ciImage = CIImage(bitmapData: data,
bytesPerRow: bytesPerRow,
size: photo.size,
format: .BGRA8,
colorSpace: CGColorSpace(name: CGColorSpace.genericRGBLinear)!)
let uiImage = UIImage(ciImage: ciImage)
print("Saving: \(uiImage.size)")
saveFile(image: uiImage) // Leaving this uncommented causes a memory crash. Leaving it commented shows no memory spike in the "Memory Report" panel.
}
}
private func saveFile(image: UIImage) {
let id = UUID()
let fileName = "\(id).png"
let data = image.pngData()!
print("Saving: \(id)")
let appSupportPath = try! FileManager.default.url(for: .applicationSupportDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: true)
let savePath = appSupportPath.appendingPathComponent(fileName)
if FileManager.default.createFile(atPath: savePath.path, contents: data, attributes: .none) {
return
}
print("Failed saving preview")
}
}
If I don't call saveFile
I don't see a spike in memory usage at all. If I do call saveFile
I get an "out of memory" error. From my understanding of how Swift/ARC allocation works, shouldn't each photo
get deallocated at the end of each iteration of the loop? When I call saveFile
, why does the memory usage buildup until the loop finishes?