iOS 什么时候会发生ANR?什么时候会发生OOM?什么时候会发生内存泄露?

在 iOS 开发中,ANR(Application Not Responding)OOM(Out Of Memory)内存泄露(Memory Leak) 是常见的性能问题。以下是它们的定义、发生原因及预防措施:


1. ANR(Application Not Responding)

定义

ANR 是指应用程序在主线程上执行耗时操作,导致界面卡顿或无响应。

发生场景

  • 主线程阻塞
    • 在主线程执行耗时操作(如网络请求、数据库查询、复杂计算)。
    • 主线程被长时间占用,无法处理 UI 事件(如点击、滚动)。
  • 死锁
    • 多个线程相互等待资源,导致主线程无法继续执行。
  • UI 更新延迟
    • 在主线程中频繁更新 UI 或执行大量布局计算。

示例代码

// 错误示例:在主线程执行耗时操作
DispatchQueue.main.async {
    for i in 0..<1000000 {
        print(i) // 模拟耗时操作
    }
}

预防措施

  • 异步执行
    • 将耗时操作放到后台线程执行。
    DispatchQueue.global().async {
        // 耗时操作
        DispatchQueue.main.async {
            // 更新 UI
        }
    }
    
  • 优化性能
    • 减少主线程的负担,避免频繁更新 UI。
  • 避免死锁
    • 合理使用锁机制,避免线程相互等待。

2. OOM(Out Of Memory)

定义

OOM 是指应用程序占用的内存超过系统限制,导致被系统强制终止。

发生场景

  • 内存占用过高
    • 加载大量图片、视频等资源。
    • 缓存未及时释放。
  • 循环引用
    • 对象之间相互强引用,导致无法释放。
  • 大对象未释放
    • 如未释放的 UIViewControllerUIView 等。

示例代码

// 错误示例:循环引用导致内存无法释放
class Person {
    var dog: Dog?
    deinit { print("Person deinit") }
}

class Dog {
    var owner: Person?
    deinit { print("Dog deinit") }
}

var person: Person? = Person()
var dog: Dog? = Dog()
person?.dog = dog
dog?.owner = person
person = nil
dog = nil // Person 和 Dog 都不会被释放

预防措施

  • 优化内存使用
    • 使用 UIImagedownsampling 技术加载大图。
    • 及时释放不再使用的资源。
  • 避免循环引用
    • 使用 weakunowned 打破循环引用。
    class Dog {
        weak var owner: Person? // 使用 weak 打破循环引用
    }
    
  • 监控内存
    • 使用 Xcode 的 Memory Graph Debugger 检查内存泄露。
    • 使用 InstrumentsAllocations 工具分析内存使用情况。

3. 内存泄露(Memory Leak)

定义

内存泄露是指应用程序中分配的内存未被释放,导致内存占用持续增加。

发生场景

  • 循环引用
    • 对象之间相互强引用,导致无法释放。
  • 未释放的闭包
    • 闭包捕获了外部变量,导致对象无法释放。
  • 未释放的单例
    • 单例对象持有大量资源,未及时释放。
  • 未移除的观察者
    • 使用 NotificationCenterKVO 后未移除观察者。

示例代码

// 错误示例:闭包捕获 self 导致内存泄露
class ViewController: UIViewController {
    var closure: (() -> Void)?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        closure = {
            self.doSomething() // 闭包捕获 self,导致内存泄露
        }
    }
    
    func doSomething() {
        print("Do something")
    }
    
    deinit {
        print("ViewController deinit")
    }
}

预防措施

  • 避免循环引用
    • 使用 weakunowned 捕获 self
    closure = { [weak self] in
        self?.doSomething()
    }
    
  • 及时释放资源
    • deinit 中释放资源。
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
    
  • 使用工具检测
    • 使用 Xcode 的 Memory Graph Debugger 检查内存泄露。
    • 使用 InstrumentsLeaks 工具分析内存泄露。

总结

问题 发生场景 预防措施
ANR 主线程执行耗时操作、死锁、UI 更新延迟 异步执行、优化性能、避免死锁
OOM 内存占用过高、循环引用、大对象未释放 优化内存使用、避免循环引用、监控内存
内存泄露 循环引用、未释放的闭包、未释放的单例、未移除的观察者 避免循环引用、及时释放资源、使用工具检测

通过合理的设计和优化,可以有效避免 ANR、OOM 和内存泄露问题,提升应用的性能和稳定性。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容