内存管理是指软件运行时对计算机内存资源的分配和使用的技术。其最主要的目的是如何高效,快速的分配,并且在适当的时候释放和回收内存资源。
引言
面试题:iOS内管管理方式是什么?使用过MRC吗?
iOS中,分有两种内存管理方法:
- ARC :自动引用计数
- MRC :手动引用计数
现在的iOS程序中,都是使用ARC了。
自动引用计数:是指内存管理中对引用采取自动计数的技术
那么问题来了:
- 哪些对象或者内容,可以使用此技术?
- 引用是什么?计数又是什么?
- iOS如何做到自动引用计数的?
内存管理 - 引用计数
引用计数是什么?
引用计数是计算机 编程语言 中的一种** 技术** 内存管理 ,是指将资源(可以是 对象 、 内存 或 磁盘 空间等等)的被 引用 次数保存起来,当被引用次数变为零时就将其释放的过程。使用引用计数技术可以实现自动资源管理的目的。
— — 截取自百度百科
引申一个iOS
内存分区 概念
因为
OC
是C语言
的超集,所以OC的内存分区模式,可以说直接用的是C语言的内存分区模型
内存分区图
如上图,内存分区一共会分为5块:
-
栈区(stack)
- 此区域存放局部变量
- 函数跳转地址及相关参数
- 此区域是从高地址到低地址分配内存的
- 此区域内容不需要程序员管理
-
堆区(heap)
- 分配我们通过alloc出的对象
- 是从低地址到高地址分配内存的
- 此区域内容必须由程序员进行管理
全局静态区
此区域必须要程序员管理常量区
此区域不需要程序员管理代码区
什么是引用计数?
通过解释可以看出,引用计数是用于内存管理的。解释中,被管理的目标是”对象”,那请问对象是什么来的?
在我们的编码过程中,需要进行内存管理的目标,分为两类:
对象 与 常量
对象:即为我们通常用alloc / new
出来的
对象中,又分:
是否被全局或者静态修饰的对象
其他普通对象
常量:就是普通的内容,基本数据类型、字符串等都有可能
对于内存分区中的介绍中,只有在堆区的内容,需要我们做内存管理。而能进入到堆区的,是对象中普通对象。
创建并分配堆内存空间的对象,需要被引用并记录的是其被引用的次数。
所以称为”引用计数“
所以,需要使用引用计数来进行管理的目标 是 存放在堆区的由程序员手动创建出来的
引用计数的作用
- 当一个对象被创建并在堆内分配内存后,对象自身将会有一个属性-引用计数,此时引用计数为1
- 当有其他对象需要持有该对象时,引用计数+1
- 当有其他对象需要释放(放弃持有)该对象时,引用计数-1
- 当该对象的引用计数为0时,系统将会销毁对象并回收其内存地址
由此可见,系统通过对象的引用计数,控制着对象的生命周期,从而实现内存空间资源管理的目的
一个小补充:
本文并不打算做代码层面的测试,虽然,我们可以通过对对象[object retainCount]
的方法来打印对象的引用计数
但是,在某些测试场景中,打印出的引用计数会是一个无限大的数,此为系统内部做了处理,且ARC下不建议直接使用retainCount
方法,所以本文暂不做研究。有兴趣的,可以自己通过打印,看看数是多少(绝大多数情况下retainCount
的数值还是正确的)~
iOS中有关引用计数的内存管理方法
iOS内存管理思考方式
- 自己生成的对象,自己所持有
id obj = [[NSObject alloc] init];
- 非自己生成的对象,自己也能持有
id obj = [NSMutableArray array];
- 不再需要自己持有的对象时释放
[obj release];
或者
[obj autorelease];
- 非自己持有的对象无法释放
了解有哪些方法,会对对象引用计数造成变更
- 生成并持有对象:alloc/new/copy/mutablecopy
如此 引用计数+1 - 持有对象 :retain
如此 引用计数+1 - 释放对象 :release/autorelease
如此 引用计数 -1 - 回收对象 :dealloc
如此 将没有引用计数
前面引言中已经说了,iOS中,基于引用计数可以引申出两种内存管理方式:
- ARC
- MRC
MRC手动引用计数
即对象的引用计数管理,交由程序员手动处理,操作系统(iOS)不做任何干预,只在对象引用计数为0时,将对象在合适时机销毁。
自行添加各种对引用计数操作的代码,这样,代码量将会增加,并一不小心,就会出现内存泄漏的问题
所以,在ARC出现后,基本上已经放弃了MRC的操作了,只要一些可能的老旧第三方库还在使用吧。(这个,也应该基本没有了,当然不排除还有些在用MRC的)
ARC自动引用计数
本质部分在引用计数式内存管理层面,ARC并没有改变,也是使用引用计数来管理对象。只是ARC自动的帮助我们处理”引用计数“相关部分
ARC独有内存管理思考方式
首先声明一点:iOS内存管理思考方式思考方式就是因为ARC的引用所带来的变化而且思考出的一个结论,此结论对ARC和MRC均有效,但ARC多了一个所有权声明
ARC — 所有权修饰符
简单的说,就是ARC下,使用所有权修饰符,告诉编译器对此对象做何种内存相关操作。
所有权修饰符有4钟:
�__strong
__weak
__unsafe_unretained
__autoreleasing
__strong
是所有id类型和对象的默认所有权修饰符
作用:强持有此对象,在对象超出其作用域时,强引用失效,释放此对象
代码演示:
--- ARC ---
{
id obj = [NSObject new];
}
--- MRC ---
{
id obj = [NSObject new];
[obj release];
}
上面两段代码等价!即出了作用域了,将obj释放
__strong
不仅可以作用在局部变量上,也可以作用在成员变量、属性上。作用一致,只是需要释放的位置,可能不同。成员变量和属性,需要在对象被释放前释放掉
__weak
__strong
所有权,将对象强持有,如果释放不当或者使用不当,将会造成APP一个名场面 :****循环引用****
所以__weak
的作用:提供弱引用,不能持有对象实例,即不会造成对象引用计数的改变
因为使用了__weak
不持有对象,当对象超出作用域后,会被立即释放
循环引用是因为有两个对象相互强引用导致,用__weak
打破两者间相互强引用的关系,即可解决循环引用的问题。
__unsafe_unretained和__autoreleasing
一个是不安全不引用修饰符,一个是自动释放修饰符
前者因为其使用会造成程序出现不确定的异常,所以基本不用
后者因为在ARC下,无法使用autorelease
方法,所以也是基本不使用
所有权修饰符和属性的修饰符对应关系如下所示:
- assign 对应的所有权类型是 __unsafe_unretained
- copy 对应的所有权类型是 __strong
- retain 对应的所有权类型是 __strong
- strong 对应的所有权类型是 __strong
- unsafe_unretained对应的所有权类型是__unsafe_unretained
- weak 对应的所有权类型是 __weak
ARC有效使用规则
- 不能使用 retain/release/retainCount/autorelease
- 不能使用NSAllocateObject/NSDeallocateObject
- 必须遵守内存管理的方法命名规则
- 不要显式调用dealloc
- 使用@autoreleasepool块代替NSAutoreleasePool
- 不能使用区域
- 对象型变量不能作为C语言结构体的成员
- 显式转换id和void *
ARC不适用场景 —— Core Foundation对象
ARC对C语言编写的Core Foundation框架中对象无效,所以,使用了该框架中的对象,需要使用其对应的引用计数方法CFRetain/CFRealease
Foundation框架与Core Foundation框架对象的区别,只是在哪个框架下生成而已,生成之后,可以在不同框架下使用。
4C717DC7-8321-48E6-B84D-A0B70C9313A2.png