跳到主要内容

内存优化策略

问题

iOS 应用如何进行内存优化?有哪些具体的优化手段?

答案

iOS 内存限制

iOS 应用没有虚拟内存交换(swap),内存不足时系统会直接 kill 应用(OOM / Jetsam)。

场景大致限制
前台应用设备 RAM 的 ~50%(如 4GB 设备 ≈ 2GB)
后台应用极低(几十 MB),超出直接被杀

核心优化策略

1. 大图优化

// ❌ 直接加载高分辨率图片 → 占用 width × height × 4 字节
let image = UIImage(named: "huge_photo") // 4000×3000 = 48MB!

// ✅ 下采样:只解码需要的尺寸
func downsample(imageAt url: URL, to pointSize: CGSize, scale: CGFloat) -> UIImage? {
let maxDimensionInPixels = max(pointSize.width, pointSize.height) * scale
let options: [CFString: Any] = [
kCGImageSourceCreateThumbnailFromImageAlways: true,
kCGImageSourceShouldCacheImmediately: true,
kCGImageSourceCreateThumbnailWithTransform: true,
kCGImageSourceThumbnailMaxPixelSize: maxDimensionInPixels
]

guard let source = CGImageSourceCreateWithURL(url as CFURL, nil),
let cgImage = CGImageSourceCreateThumbnailAtIndex(source, 0, options as CFDictionary) else {
return nil
}
return UIImage(cgImage: cgImage)
}

2. 内存警告处理

class ImageCache {
private var cache = NSCache<NSString, UIImage>()

init() {
NotificationCenter.default.addObserver(self,
selector: #selector(clearCache),
name: UIApplication.didReceiveMemoryWarningNotification,
object: nil)
}

@objc func clearCache() {
cache.removeAllObjects()
}
}

3. NSCache 替代 Dictionary

// NSCache:
// - 自动在内存不足时淘汰
// - 线程安全
// - 可设置 countLimit / totalCostLimit
let cache = NSCache<NSString, UIImage>()
cache.countLimit = 100
cache.totalCostLimit = 50 * 1024 * 1024 // 50MB

4. autoreleasepool 降低峰值

func processLargeDataset(_ data: [Data]) {
for item in data {
autoreleasepool {
let processed = transform(item) // 临时对象在每次循环结束释放
save(processed)
}
}
}

5. 延迟加载与按需释放

class DetailViewController: UIViewController {
// 延迟加载
lazy var heavyView: HeavyView = {
HeavyView()
}()

// 进入后台时释放非必要资源
@objc func didEnterBackground() {
heavyView.clearCache()
}
}

常见面试问题

Q1: 如何监控应用的内存使用量?

答案

func currentMemoryUsage() -> UInt64 {
var info = mach_task_basic_info()
var count = mach_msg_type_number_t(MemoryLayout<mach_task_basic_info>.size) / 4
let result = withUnsafeMutablePointer(to: &info) {
$0.withMemoryRebound(to: integer_t.self, capacity: Int(count)) {
task_info(mach_task_self_, task_flavor_t(MACH_TASK_BASIC_INFO), $0, &count)
}
}
return result == KERN_SUCCESS ? info.resident_size : 0
}

Q2: OOM 崩溃如何排查?

答案:OOM 不会产生 crash log。排查方法:

  1. MetricKit(iOS 14+):MXForegroundExitData/MXBackgroundExitData 提供 OOM 退出信息
  2. Instruments Allocations:Mark Generation 对比内存增量
  3. 内存报告:Xcode Organizer → Memory 查看内存峰值
  4. mmap 大文件替代全量加载

Q3: UIImage 为什么占用那么多内存?

答案UIImage 解码后的内存 = width × height × 4 bytes(RGBA)。一张 4000×3000 的图片 = 48MB。优化方式:下采样(只解码所需尺寸)、使用 UIGraphicsImageRenderer 而非 UIGraphicsBeginImageContext

相关链接