【iOS】引用计数
文章目录
- 【iOS】引用计数
- 前言
- ARC与MRC
- 什么是引用计数的机制
- 内存管理的思考方式
- 自己生成的对象
- 非自己生成的对象
- 不再需要自己持有就释放
- 无法释放非自己持有的对象
- autorelease
- 小结
前言
笔者最近开始学习了一下有关于引用计数的内容,写这篇博客来简单认识一下引用计数部分的内容。
ARC与MRC
ARC就是自动引用计数,指的是OC中对于引用计数采取一个自动计数的方式。
在Objective-C中采用Automatic Reference Counting
(ARC)机制,让编译器来进行内存管理。在新一代Apple
LLVM编译器中设置ARC为有效状态,就无需再次键入retain或者release代码,这在降低程序崩溃、内存泄漏等风险的同时,很大程度上减少了开发程序的工作量。编译器完全清楚目标对象,并能立刻释放那些不再被使用的对象。如此来,应用程序将具有可预测性,且能流畅运行,速度也将大幅提升。
MRC则是由程序员手动进行内存管理,很显然ARC极大的提高了开发效率,也可以让程序更加流畅的运行。
什么是引用计数的机制
我们可以通过一个开关灯的思路去理解这部分内容,
正如上图所展示的,我们办公室在第一个人来的时候就要开灯,在最后一个人走后就要关灯,不能在当一个人走后就进行一个关灯操作,这样会影响其他人工作,所以只有当最后一个人离开办公室的时候再关灯就好了,这样才可以让其他人正常工作,而我们创建的对象可以理解为办公室里的照明设备,只要有一个人工作,照明设备就不可以被关闭。
OC中对应的一个状态 | 对照明设备的操作 |
---|---|
生成对象 | 开灯 |
持有对象 | 办公室还有人需要照明 |
释放对象 | 不需要照明 |
废弃对象 | 关灯 |
可以看到这里对象是否要被释放和我们这里办公室里面是否还有人关系非常密切,所以OC就采用了一种引用计数的方式来记录该对象是否要被释放,我们分析一下灯的开关被打开的几个过程,首先开始是没有人的状态这时候引用计数为0,后面有第一个人之后,引用计数为1,然后又来一个人引用计数为2,然后有人离去的时候引用计数减一,最后一个人离开的时候引用计数再减一,这时候我们就可以把这个灯关掉,这里我们把上述的灯改成对象,所以这里就简单介绍了我们对象的概念。
下面这张图就展示了我们的引用计数这个机制的几个步骤。
内存管理的思考方式
我们思考内存管理的时候,往往会过于关注计数这个问题,但是实际上计数反而不是重点,我们真正的应该思考的应该是如下四个方式来分引用计数。
- 自己生成的对象,自己持有;
- 非自己生成的对象,自己也能持有;
- 不再需要自己持有的对象时释放;
- 非自己持有的对象无法释放。
这个表格给出了OC中对应的操作。
自己生成的对象
id obj = [[NSObject alloc] init];
和id obj = [NSObject new];
这两个都是自己生成对象自己持有。
我们按着驼峰命名法也可以来自己生成并且持有对象。
非自己生成的对象
我们使用出来非上述的方式也可以取得一个对象,但是因为不是通过自己生成并且持有的方式创建的,所以我们通过类似[NSMutableArray array]
的方式创建的,我们还没有实现持有这个步骤,所以我们需要进行一个持有的过程.
id obj = [NSMutableArray array];
[obj retain];
这个方法的底层实现原理其实是通过一个autorealse
这个方式来实现的,至于这个方法是如何实现的我们后面简单介绍一下。
id obj = [[NSObject alloc] init];
[obj autorelease];
这里就实现一个持有的操作,这里的对象并非自己创建但是仍然可以持有该对象。
不再需要自己持有就释放
简单来说就是我们不需要持有这个对象的时候,我们就需要调用release
这个代码来实现一个释放的效果,比方说:
id obj = [NSMutableArray array];
[obj retain];
[obj release];
这里注意一下,如果一个对象一经释放就不可以再访问了。
无法释放非自己持有的对象
在OC中我们也无法释放非自己持有的对象,举个例子:
id obj = [[NSObject alloc] init];
[obj release];
[obj release];
根据前面我们说的内容,这里我们如果已经release
过一次,obj所持有的对象已经被废弃了,所以我们在释放一次的话就会出现一个程序崩溃的问题。
其实我们可以总结为以下四个方面:
autorelease
我们上面提到了autorelease
,这里我们就来简单介绍一下有关autorelease的内容。
而autorelease是一种延迟释放对象的机制。当一个对象被autorelease时,它的引用计数不会立即减少,而是在当前autorelease pool被销毁时才会减少。
这意味着,即使你不再使用一个对象,它也不会立即被释放,而是等待当前Runloop结束时才会被释放。因此,autorelease可以在需要延迟释放对象时使用,以避免频繁释放和创建对象带来的性能开销。
他最大好处在于可以使取得的对象存在但是自己并不持有对象。
我们可以把它理解成一个C语言中的局部变量,C语言中的局部变量是在我们的变量超出自己的作用域的时候会被自动废弃,那么这里则是一个对象如果离开了autorelease的池中他会自己调用release
这个方法。下面给出如何使用autorelsease
,他有一优势就是我们可以定义变量的作用域。
这里我们按照上图的要求编写下面这段代码:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj = [[NSObject alloc] init];
[obj autorelease];
[pool drain];
NSLog(@"%lu", [obj retainCount]);//这里会报错,因为我们无法访问到这个对象
上面这段代码展示了我们如何使用autorelease
这个方法,这里的[pool drain];
相当于[obj release]
但是实际上我们的编译器会自己创建NSAutoreleasePool
这个类的对象,就好比我们的NSMutableArray
这个类
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj = [NSMutableArray array];
[obj autorelease];
[pool drain];
这段代码会报错,主要原因是我们在创建NSMutableArray这个类的对象的时候,编译器会自动创建一个NSAutoreleasePool
然后这个池子被放进了我们之前手动创建的NSAutoreleasePool
这个池子中,所以他会把池子给释放,然后就会释放我们的数组,从而导致了我们的数组引用计数已经为0,导致代码报错。
小结
这里笔者对于引用计数进行了一个简单的学习,简单了解了他的一个相关实现,后面还会继续进行补充学习。