type
status
date
slug
summary
tags
category
icon
password
本文将会先对iOS 内存管理涉及到一些基础的概念、技术,进行简单描述,我们将在后面系列篇章中对相关知识点进行更为深入的探讨。
一、iOS 内存管理
iOS 内存管理,或者说任何一门语言的内存管理,都是程序员必须深刻理解,也是各种面试中最常见的话题。
iOS 内存管理中,在iOS 5以前,需要手动管理内存,即MRC(Manual Reference Counting)。
2011年,苹果推出了ARC(Automatic Reference Counting)技术,程序员不再需要手动管理内存,对开发效率的提升以及App的稳定性都有了显著提升。
不管是MRC,还是ARC,苹果内存管理的本质保持不变——就是OC对象的引用计数的管理。引用计数的概念同C++的智能指针概念很类似,编译期以及运行时会帮助我们自动来维护一个对象的生与死。
所以说,引用计数是iOS 内存管理的根基所在,所有和内存管理相关的最后都是引用计数的问题。
二、内存布局
在进入iOS内存管理的具体技术细节之前,我们需要先了解各种各样变量以及对象在内存中的布局。对此进行了下面测试(在iPad Pro真机上测试,不同设备,每次测试结果可能有差异),对应代码--内存布局:
由此,我们绘制了如下简单的内存布局图:
下图中,有一个地方需要注意:打印出的对象地址,即分配在堆上的地址,其实比栈地址更大。但在下图中则不是这样。下图描述的是一般性规则,但在不同的操作系统和编译期实现时,则各有不同,另外在堆中分配地址虚拟内存往往更大。
当然,OC语言源于C,其内存布局是类似的,如下是C语言对于内存中布局的一张图(by Iran 来源网络):
图中更为详细对部分段进行阐述:
- .bss:存放未初始化数据的全局变量,以及static修饰的变量;
- .data:存放已经初始化的的全局变量,以及static修饰的变量;
- .text:存放代码,以及const等常量,这种常量包括const修饰符所修饰的,以及常量字符串。
三、内存管理的技术
3.1 Tagged Pointer
Tagged Pointer技术,就是在分配给对象的指针里,直接存储该对象的数据,并且以标记来区分是什么类型的数据。
使用Tagged Pointer技术的对象,没有引用计数这回事,不需要进行retain、release等相关操作。甚至在超过变量的作用域之后,仍然能访问。
可以使用Tagged Pointer技术的类有:
NSNumber
、NSString
、NSDate
等。比如NSNumber
在内存中存储如下:将在下一篇中内存管理(二)Tagged Pointer,详细介绍该技术。
3.2 优化的isa指针
在64位设备推出后,苹果发现64位对象的
isa指针
是很浪费的,所以针对其进行了改良,改良结果如下:上面就是优化过的
isa指针
,优化过的isa指针
具备存储引用计数的作用,利用其高19位extra_rc
来存储引用计数。但是如果
extar_rc
不够存储的话,就需要将引用计数存入一个叫Side Table
的数据结构中。如果还不够,so sorry,你该反思下为什么会出现这种奇葩的事情。
3.3 引用计数
说回iOS 内存管理的根基——引用计数。
针对上面引用计数,
NSObject
中提供了对应的方法来对其进行控制:对象的生命周期
针对上面引用计数的阐述,相信大家对对象的引用计数都有一个基本的了解,那么对象的引用计数又是如何与对象的生命周期关联的呢?
四、属性
在开发中,属性太常用了,以至于无属性,不会写代码。
4.1 什么是属性
属性(property)是Objective-C语言的一个特性,并且用
@property
进行声明。使用
@property
声明的变量,编译期将会自动为它生成对应的读写方法,即setter与getter方法。并且,允许开发者指定@property
的参数,包括原子性、内存管理语义一系列特质,根据不同的特质,编译期将会对setter
和getter
做不同的实现。属性本质可简单归结于:属性=成员变量+
setter
+getter
,但又不止于此,针对属性特质的丰富变化,才是开发者的利器。4.2 @property
特质
在这里,我们只说明与内存管理相关的属性特质,如下:
五、内存问题
苹果为开发者提供了如此方便的计数,开发者又是如此的优秀。怎么会出问题。
内存问题还是会有的,一般分为两位:访问坏内存以及循环引用。
5.1 访问坏内存
访问坏内存,也分为两部分:
5.1.1 野指针
内存地址的对象已经释放后,再进行访问,就会产生的野指针问题,可能会引起崩溃;
5.1.2 空指针
访问的内存地址的对象释放了,也置为nil了。再次访问拿不到任何结果,产生空指针问题。
5.2 内存泄漏
5.2.1 内存泄漏
内存泄漏,是指没有释放掉不再引用的对象。例如在MRC下:
但是,针对上面这种情况,在ARC下,编译期会自动帮我们释放。
在ARC下,另外一种更为常见的内存泄漏——循环引用。
5.2.2 循环引用
循环引用,当两个不同的对象各有一个强引用指向对方,那么就产生了循环引用,当然三个、四个对象产生的环循环引用也是同样的。
可以简单理解为A引用B,而B又引用了A,双方都同时保持对方的一个引用,导致任何时候引用计数都不为0,始终无法释放。
我们将在后面的一篇内存管理(七)循环引用 详述该问题。
5.3 分析
我们可以通过Xcode提供或其他工具来分析以上问题。
5.3.1 僵尸对象的监听
5.3.2 Instruments
如下图,Instruments提供了一系列的工具来分析App的方方面面,图中圈起来的跟内存相关的工具。
5.3.3 FBMemoryProfiler
facebook出品的内存检测工具,GitHub地址。
参考
链接
示例代码