ObjC内存管理
# 04.ObjC内存管理
# 目录介绍
- 4.1 生命周期设计
- 4.1.1 初始化操作
- 4.1.2 销毁时释放
- 4.2 内存管理
- 4.2.1 引用计数
- 4.2.2 引用计数规则
- 4.2.3 内存管理方式
- 4.3 手动引用计数
- 4.3.1 MRC案例
- 4.3.2 MRC案例分析
- 4.3.3 原理设计
- 4.3.4 使用自动释放池
- 4.4 自动引用计数
- 4.4.1 ARC案例
- 4.4.2 ARC案例分析
- 4.4.3 原理设计
- 4.5 内存最佳实践
- 4.5.1 避免循环引用
- 4.5.2 管理临时对象
- 4.5.3 dealloc
- 4.5.4 MRC vs ARC
# 4.1 生命周期设计
对象的生命周期是指对象从创建到销毁的整个过程。了解对象的生命周期对于正确管理内存和资源以及避免潜在的问题非常重要。
# 4.1.1 初始化操作
1.分配(Allocation):在分配阶段,通过使用alloc和init方法或其他创建对象的方法,为对象分配内存空间,并初始化对象的实例变量。
MyObject *obj = [[MyObject alloc] init];
- alloc:为对象分配内存。
- init:初始化对象,设置默认值或执行其他初始化操作。
2.初始化(Initialization):在初始化阶段,可以在init方法中对对象的实例变量进行初始化,设置初始状态和执行其他必要的操作。
- (instancetype)init {
self = [super init];
if (self) {
// 初始化实例变量
}
return self;
}
// 任何类在实例化之前都会先调用该方法
+ (void)initialize {
//这个比init方法先调用
}
2
3
4
5
6
7
8
9
10
11
12
# 4.1.2 销毁时释放
1.释放(Deallocation):在释放阶段,对象不再被使用,内存空间将被释放。当对象不再被引用时,Objective-C的自动引用计数(ARC)机制会自动处理对象的释放。
obj = nil; // 对象将被释放
2.释放前的清理(Cleanup before Deallocation):在对象被释放之前,可以在dealloc方法中执行一些清理操作,如释放资源、取消观察者、解除通知等。
// 对应于 initialize 方法,用于清空对象,当引用计数为 0 时被调用
- (void)dealloc {
// 如果未启用 ARC,则需要手动释放引用计数
[nickName release];
// 清理操作
[super dealloc];
}
2
3
4
5
6
7
# 4.2 内存管理
# 4.2.1 引用计数
引用计数是 Objective-C 内存管理的核心机制。每个对象都有一个引用计数器,用于记录当前有多少个对象在使用它。
# 4.2.2 引用计数规则
- 当对象被创建时,引用计数为 1。
- 当对象被其他对象引用时,引用计数加 1。
- 当对象不再被引用时,引用计数减 1。
- 当引用计数为 0 时,对象被销毁,内存被释放。
# 4.2.3 内存管理方式
内存管理是一项重要的任务,用于管理对象的生命周期和内存分配。在Objective-C中,有两种主要的内存管理方式:
手动引用计数(Manual Reference Counting,MRC)和自动引用计数(Automatic Reference Counting,ARC)。
# 4.3 手动引用计数
# 4.3.1 MRC案例
手动引用计数(MRC):在MRC中,开发者需要手动管理对象的引用计数,通过调用retain、release和autorelease等方法来增加或减少对象的引用计数。需要确保在不再使用对象时,适时地释放其内存。
MyObject *obj = [[MyObject alloc] init]; // 引用计数为1
[obj retain]; // 增加引用计数
//释放对象
[obj release]; // 减少引用计数
[obj autorelease]; // 在自动释放池中延迟释放
2
3
4
5
# 4.3.2 MRC案例分析
手动管理引用计数的方法
retain:增加对象的引用计数。release:减少对象的引用计数。autorelease:将对象添加到自动释放池,稍后释放。dealloc:对象被销毁时调用的方法,用于释放资源。
// 创建对象,引用计数为 1
NSObject *obj = [[NSObject alloc] init];
// 增加引用计数
[obj retain]; // 引用计数为 2
// 减少引用计数
[obj release]; // 引用计数为 1
// 将对象添加到自动释放池
[obj autorelease]; // 引用计数为 1,稍后释放
// 手动释放对象
[obj release]; // 引用计数为 0,对象被销毁
2
3
4
5
6
7
8
9
10
11
12
13
14
# 4.3.3 原理设计
# 4.3.4 使用自动释放池
自动释放池用于延迟对象的释放。当对象被发送 autorelease 消息时,它会被添加到当前的自动释放池中。当自动释放池被释放时,池中的所有对象都会收到 release 消息。
@autoreleasepool {
// 创建对象并添加到自动释放池
NSString *str = [[[NSString alloc] initWithFormat:@"Hello, %@", @"World"] autorelease];
// 使用对象
NSLog(@"%@", str);
// 自动释放池结束时,str 会被释放
}
2
3
4
5
6
7
8
9
# 4.4 自动引用计数
ARC 是 Objective-C 的编译器特性,它自动管理对象的引用计数,开发者无需手动调用 retain、release 和 autorelease。
# 4.4.1 ARC案例
自动引用计数(ARC):在ARC中,编译器会自动插入适当的引用计数管理代码,开发者无需手动管理对象的引用计数。编译器会根据代码的上下文自动添加retain、release和autorelease等操作。
MyObject *obj = [[MyObject alloc] init]; // 引用计数为1
obj = nil; // 引用计数 = 0,对象被销毁
// 不需要手动调用retain/release/autorelease
2
3
# 4.4.2 ARC案例分析
ARC 的规则
- 编译器会自动在适当的位置插入
retain、release和autorelease。 - 开发者不能手动调用
retain、release和autorelease。 - 对象的生命周期由编译器管理。
# 4.4.3 原理设计
ARC:编译器自动插入 retain/release(默认模式)
属性修饰符:
strong:持有对象(默认)weak:弱引用(自动置 nil)copy:复制对象(用于 NSString/NSArray)assign:基本数据类型(非对象)
1.强引用(Strong Reference):默认情况下,对象之间的引用是强引用,即强制保持对对象的引用,只有当所有强引用都被释放时,对象才会被释放。
// 默认值,变量会被保存在内存中直到离开作用域
__strong NSString *strongString;
2
2.弱引用(Weak Reference):使用__weak修饰符可以创建弱引用,弱引用不会增加对象的引用计数,当对象被释放时,弱引用会自动设置为nil。
// 弱引用,假如引用的对象被释放,那么弱引用会被设为 nil
__weak MyObject *weakObj = obj;
// 与弱引用类似,但是如果引用的对象被释放,引用也不会被设为 nil
__unsafe_unretained NSArray *unsafeArray;
2
3
4
5
3.循环引用(Retain Cycle):循环引用指的是两个或多个对象之间相互持有强引用,导致它们无法被释放。为了避免循环引用,可以使用__weak修饰符、__unsafe_unretained修饰符或使用弱引用的代理对象等方式。
__weak typeof(self) weakSelf = self;
4.手动释放内存:在MRC中,需要手动释放对象的内存,可以在适当的时机调用release方法。在ARC中,不需要手动释放内存,编译器会自动插入释放代码。
# 4.5 内存最佳实践
# 4.5.1 避免循环引用
循环引用会导致内存泄漏。使用 weak 引用或 __weak 修饰符来打破循环引用。
__weak typeof(self) weakSelf = self;
self.block = ^{
[weakSelf doSomething]; // 使用 weakSelf 避免循环引用
};
2
3
4
# 4.5.2 管理临时对象
在循环中创建大量临时对象时,使用 @autoreleasepool 及时释放内存。
for (int i = 0; i < 1000; i++) {
@autoreleasepool {
NSString *tempStr = [NSString stringWithFormat:@"Temp %d", i];
NSLog(@"%@", tempStr);
}
}
2
3
4
5
6
# 4.5.3 dealloc
在 dealloc 方法中释放对象持有的资源。
# 示例:
- (void)dealloc {
// 释放资源
[_resource release];
[super dealloc];
}
2
3
4
5
# 4.5.4 MRC vs ARC
手动内存管理与 ARC 的对比
| 特性 | 手动内存管理 | ARC |
|---|---|---|
| 引用计数管理 | 手动调用 retain、release | 编译器自动管理 |
| 代码复杂度 | 较高,容易出错 | 较低,减少内存管理错误 |
| 性能 | 需要手动优化 | 编译器优化,性能较好 |
| 适用场景 | 需要精细控制内存的场合 | 大多数现代 Objective-C 项目 |