iOS 内存管理相关总结

内存管理是指软件运行时对计算机内存资源的分配和使用的技术。其最主要的目的是如何高效,快速的分配,并且在适当的时候释放和回收内存资源。

引言

面试题:iOS内管管理方式是什么?使用过MRC吗?

iOS中,分有两种内存管理方法:

  • ARC :自动引用计数
  • MRC :手动引用计数

现在的iOS程序中,都是使用ARC了。

自动引用计数:是指内存管理中对引用采取自动计数的技术

那么问题来了:

  1. 哪些对象或者内容,可以使用此技术?
  2. 引用是什么?计数又是什么?
  3. iOS如何做到自动引用计数的?

内存管理 - 引用计数

引用计数是什么?

引用计数是计算机 编程语言 中的一种** 技术** 内存管理 ,是指将资源(可以是 对象内存磁盘 空间等等)的被 引用 次数保存起来,当被引用次数变为零时就将其释放的过程。使用引用计数技术可以实现自动资源管理的目的。
— — 截取自百度百科

引申一个iOS 内存分区 概念

因为OCC语言的超集,所以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中,基于引用计数可以引申出两种内存管理方式:

  1. ARC
  2. 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有效使用规则

  1. 不能使用 retain/release/retainCount/autorelease
  2. 不能使用NSAllocateObject/NSDeallocateObject
  3. 必须遵守内存管理的方法命名规则
  4. 不要显式调用dealloc
  5. 使用@autoreleasepool块代替NSAutoreleasePool
  6. 不能使用区域
  7. 对象型变量不能作为C语言结构体的成员
  8. 显式转换id和void *

ARC不适用场景 —— Core Foundation对象

ARC对C语言编写的Core Foundation框架中对象无效,所以,使用了该框架中的对象,需要使用其对应的引用计数方法CFRetain/CFRealease

Foundation框架与Core Foundation框架对象的区别,只是在哪个框架下生成而已,生成之后,可以在不同框架下使用。


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