iOS内存管理
一、iOS 内存管理总体模型
1️⃣ 物理内存 vs 虚拟内存
• 物理内存(RAM):真实存在的内存
• 虚拟内存:
• 每个进程看到的是连续地址空间
• iOS 使用 VM + 页(page)机制
• 页大小:16KB(arm64)
iOS 特点
• ❌ 没有 swap 到磁盘
• ✅ 内存压力大 → 直接杀进程
• 优先级:
1. 前台 App
2. 后台音频 / 导航
3. 后台普通 App(直接 kill)
👉 结论:
iOS 内存泄漏 ≠ 变慢,而是 闪退 / 被系统杀
二、ARC(Automatic Reference Counting)
1️⃣ ARC 本质
ARC 不是 GC,是 编译期插入 retain / release
[obj retain];
[obj release];
由编译器在合适位置自动插入。
2️⃣ 引用计数规则(核心)
3️⃣ dealloc 调用时机
• 引用计数 == 0
• 不会立即释放内存,而是:
• 调用 dealloc
• 标记可回收
• 由 RunLoop 合适时机回收
⚠️ 面试点
dealloc 不一定立即 free 内存
三、属性修饰符与内存
1️⃣ strong / weak / assign / copy
strong
@property (nonatomic, strong) NSObject *obj;
• 持有对象
• 参与引用计数
• 默认 ARC 下对象属性
weak
@property (nonatomic, weak) NSObject *obj;
• 不增加引用计数
• 对象释放 → 自动置 nil(runtime 支持)
• 解决循环引用
⚠️ weak 底层:
• runtime 维护 weak table
• dealloc 时清空所有 weak 指针
assign
• 不管理生命周期
• 常用于:
• 基本数据类型
• 非 ARC / unsafe_unretained
🚨 危险
@property (nonatomic, assign) NSObject *obj; // 野指针风险
copy(非常高频)
@property (nonatomic, copy) NSString *name;
作用:
• 防止可变对象被外部修改
NSMutableString *str = ...
self.name = str;
[str appendString:@"xxx"]; // 不影响 name
• NSString / block / NSArray / NSDictionary 推荐 copy
2️⃣ block 的内存管理(重点)
block 类型
ARC 下
• block 赋值给 strong / copy → 自动拷贝到堆
self.block = ^{
NSLog(@"%@", self);
};
⚠️ 循环引用
self.block = ^{
self.name = @"abc";
};
解决:
__weak typeof(self) weakSelf = self;
self.block = ^{
weakSelf.name = @"abc";
};
四、自动释放池(AutoreleasePool)
1️⃣ autorelease 是什么?
NSString *str = [NSString stringWithFormat:@"abc"];
• 对象加入 当前 autorelease pool
• pool drain 时统一 release
2️⃣ RunLoop 与 AutoreleasePool
系统自动创建 pool:
• 每次 RunLoop 开始
• 每次 RunLoop 休眠前
kCFRunLoopEntry
→ 创建 pool
kCFRunLoopBeforeWaiting
→ drain pool
3️⃣ 为什么 for 循环要手动加 pool?
for (...) {
@autoreleasepool {
// 大量临时对象
}
}
否则:
• 所有临时对象堆积到一次 RunLoop
• 瞬间内存暴涨
📌 音视频 / 图片处理 必考 + 必踩坑
五、循环引用(内存泄漏核心来源)
1️⃣ 常见循环引用场景
对象 ↔ block
self.block = ^{
self.doSomething;
};
delegate
@property (nonatomic, strong) id
应为:
@property (nonatomic, weak) id
2️⃣ NSTimer 循环引用(高频)
self.timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:@selector(tick)
userInfo:nil
repeats:YES];
形成:
self → timer → self
解决方案:
• block + weak
• 中间 proxy(NSProxy)
3️⃣ CADisplayLink
和 NSTimer 一样的问题,更隐蔽
六、内存分区
七、内存警告(Memory Warning)
1️⃣ 系统何时发?
• 可用内存低
• VM 压力过大
2️⃣ 响应方式
UIViewController
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
应该做什么?
• 释放:
• 缓存
• 图片
• 可重建数据
❌ 不要释放:
• 正在使用的数据
• 核心模型数据
八、内存泄漏 vs 野指针
内存泄漏
• 对象无法释放
• 内存持续增长
• 常见原因:循环引用
野指针
• 对象已释放
• 指针仍访问
• EXC_BAD_ACCESS
泄漏是“活着但没用”,野指针是“死了还用”
九、工具
1️⃣ Instruments
Leaks
• 查循环引用
• 非 ARC / CoreFoundation 泄漏
Allocations
• 内存增长趋势
• 对象生命周期
Zombies(调试)
• 捕获野指针
• 定位已释放对象访问
2️⃣ Xcode Memory Graph
• 一眼看引用链
• block / delegate / timer 一清二楚
1️⃣ C 结构体不受 ARC 管理
AVFrame *frame = av_frame_alloc();
👉 必须:
av_frame_free(&frame);
2️⃣ CVPixelBuffer / OpenGL 资源
• 必须手动释放
• 否则 GPU 内存泄漏(系统更狠,直接 kill)
转载请注明:iTwinkle的博客 » iOS内存管理