1

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

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

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

4

1 回答 1

0

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

提供的解决方案可用于两者

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

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

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

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

以下是您可以在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)
    }

其中URL's.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
        }
    }
}
于 2021-10-01T07:24:50.580 回答