I have implement function for upload image but before upload I will preview image to user.
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult])
But suddenly I select image to preview, memory usage will super high change from 30mb to 480mb. It was happened after I set image to uiview. memory will depend on image size if big size my memory will get high too.
CodePudding user response:
This is because i assume you take the result from the picker in UIImage
and set it to the UIImageView
. UIImage
s could have a really big memory usage. For example if the picked image is 12MP (3024 x 4032 = 12 192 768), as the OS creates UIImage
instance for it, for each of the 12192768 pixels from the photo, 4 bytes are used in memory - one for alpha, one for red, one for green and one for the blue component.
This means that in the phone memory, 12192768 * 4 = 48 771 072 bytes (48 mb) will be used. Its worth pointing out that this size is not the same as the one that the image is using when its on your disk drive, as when it is stored there its usually compressed with some algorithm like JPG or PNG.
I hope this gives you an insight on what the issue is.
One more efficient way to handle this is to get the picked image as URL
from the disk without instantiating UIImage
at all, and then from it, load a shrunken down representation from it into a UIImage
, just for presentation to the user on its small phone screen, while uploading the original image. In some cases you might even decide to use the shrunken down image as it might be enough for your use case.
Here's how you might do that in your picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult])
from UIImagePickerControllerDelegate
:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
guard let url = info[.imageURL] as? URL else { return } // taking the result as `url` instead of uiimage
if let image = url.asSmallImage {
self.loadImage(image) // do whatever with the small image
}
picker.dismiss(animated: true)
}
where URL
's .asSmallImage
is defined as:
// MARK: - Memory Optimized Image Loading
extension URL {
/// Used for limiting memory usage when opening new photos from user's library.
///
/// Photos could consume a lot of memory when loaded into `UIImage`s. A 2000 by 2000 photo
/// roughly will consume 2000 x 2000 x 4 bytes = 16MB. A 10 000 by 10 000 photo will consume
/// 10000 * 10000 * 4 = 400MB which is a lot, give that in your app
/// you could pick up more than one photo (consider picking 10-15 photos)
var asSmallImage: UIImage? {
let sourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
guard let source = CGImageSourceCreateWithURL(self as CFURL, sourceOptions) else { return nil }
let downsampleOptions = [
kCGImageSourceCreateThumbnailFromImageAlways: true,
kCGImageSourceCreateThumbnailWithTransform: true,
kCGImageSourceThumbnailMaxPixelSize: 2_000,
] as CFDictionary
guard let cgImage = CGImageSourceCreateThumbnailAtIndex(source, 0, downsampleOptions) else { return nil }
let data = NSMutableData()
guard let imageDestination = CGImageDestinationCreateWithData(data, kUTTypeJPEG, 1, nil) else { return nil }
// Don't compress PNGs, they're too pretty
let destinationProperties = [kCGImageDestinationLossyCompressionQuality: cgImage.isPNG ? 1.0 : 0.75] as CFDictionary
CGImageDestinationAddImage(imageDestination, cgImage, destinationProperties)
CGImageDestinationFinalize(imageDestination)
let image = UIImage(data: data as Data)
return image
}
}