【问题标题】:Extreamly memory usage when picker image from UIImagePickerViewController?来自 UIImagePickerController 的选择器图像时内存使用量非常大?
【发布时间】:2021-11-22 04:16:16
【问题描述】:

我已经实现了上传图片的功能,但是在上传之前我会预览图片给用户。

func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) 

但突然我选择图片预览,内存使用量会超高从 30mb 变为 480mb。 这是在我将图像设置为 uiview 之后发生的。如果大尺寸我的内存也会变高,内存将取决于图像大小。

【问题讨论】:

    标签: ios image uiimage uipickerviewcontroller


    【解决方案1】:

    进行一个小的编辑,以更正答案,如在问题标题中您要求UIImagePickerController,但在问题文本中引用了来自PHPickerViewController 的委托方法。这两个控制器均由 Apple 提供,用于处理从照片库中挑选图像,PHPickerViewController 是 iOS 14+ 中可用的较新控制器,取代了旧 iOS 版本上使用的旧 UIImagePickerController。 em>

    提供的解决方案可以同时用于他们两个

    这是因为我假设您从UIImage 中的选择器中获取结果并将其设置为UIImageViewUIImages 可能会占用大量内存。例如,如果拾取的图像是 12MP (3024 x 4032 = 12 192 768),当操作系统为其创建 UIImage 实例时,对于照片中的 12192768 个像素中的每一个,在内存中使用 4 个字节 - 一个用于 alpha,一种用于红色,一种用于绿色,一种用于蓝色。

    这意味着在手机内存中,将使用 12192768 * 4 = 48 771 072 字节 (48 mb)。值得指出的是,这个大小与图像在磁盘驱动器上使用的大小不同,因为当它存储在那里时,通常使用 JPG 或 PNG 等算法进行压缩。

    我希望这能让您了解问题所在。

    处理此问题的一种更有效的方法是从磁盘获取选取的图像为URL,而根本不实例化UIImage,然后从中将缩小的表示从它加载到UIImage,只需用于在其小手机屏幕上展示给用户,同时上传原始图像。在某些情况下,您甚至可能决定使用缩小的图像,因为它可能足以满足您的用例。

    以下是您在 picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) 中从 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)
        }
    

    其中URL.asSmallImage 定义为:

    // 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
        }
    }
    

    其中cgImage.isPNG定义为:

    // MARK: - Helpers
    
    extension CGImage {
        
        /// Gives info whether or not this `CGImage` represents a png image
        /// By observing its UT type.
        var isPNG: Bool {
            if #available(iOS 14.0, *) {
                return (utType as String?) == UTType.png.identifier
            } else {
                return utType == kUTTypePNG
            }
        }
    }
    

    【讨论】:

    • 感谢@Stoyan 的最佳解释。我已经应用了您的代码并创建了对我的应用程序中的所有资产进行去采样的函数,它的工作很酷。 App 的内存使用减少了大约 40% +++ 这很好。
    猜你喜欢
    • 2015-07-06
    • 2013-05-05
    • 2012-07-24
    • 1970-01-01
    • 1970-01-01
    • 2012-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多