Objective-C的内存管理实质上就是引用计数。
从前是手动引用计数(MRC),现在是自动引用计数(ARC)。
所谓ARC,就是让编译器来进行内存管理,现在Xcode默认ARC为有效状态。
引用计数:
Objective中,生成对象时引用计数为1,持有对象引用计数加一,释放对象引用计数减一,当对象引用计数为0时则释放对象。
在ARC下我们不必关心引用计数这个概念。但是在MRC下必须清晰引用计数,而且要牢记:自己生成的对象自己持有,非自己生成的对象自己也能持有,不再需要自己持有的对象要释放,非自己持有的对象不能释放。下面详细说明:
MRC下:
拿数组来说,可以使用这两种方式创建:一种是使用alloc方法,另一种是使用array等一系列方法。那这两种方法有什么区别:
使用alloc方法生成的对象自己持有(持有的概念通过下面的对比会清楚),当不再需要时自己释放(调用release方法),当一个对象的引用计数为0时不可以再让变量调用release(非自己持有的对象不能释放)。
使用array方法只是取得对象的存在,就是说你可以通过变量访问这个对象,但是你不持有它,当你不再需要使用这个对象时你不必调用release方法。你可以对变量调用retain方法来持有对象,当你不需要使用对象时调用release方法释放对象。
为什么通过array方法获得的对象有这个特性?
首先说明这不是ARC。这就要介绍autorelease了。
我们使用release方法会立即释放一个对象,而使用autorelease方法会将对象注册到autoreleasepool中,具体的步骤如下:
生成NSAutoreleasePool对象,对不需要被持有的对象调用autorelease方法,废弃NSAutoreleasePool对象。当NSAutoreleasePool对象被废弃时,调用过autorelease方法的对象都会被释放。在iOS开发中,主线程的NSRunLoop负责对自动释放池生成废弃,这里不再赘述,当然我们可以自己创建自动释放池,及时废弃大量占用内存的对象。需要注意一点:无论哪种类型的变量调用autorelease方法、都是调用NSObject类的autorelease方法,但是NSAutoreleasePool类的autorelease方法已被该类重载,如果NSAutoreleasePool的对象调用autorelease方法时会出现运行时错误。
另外使用以下方法、或以以下方法开头时意味着自己生成的对象自己持有:alloc、new、copy、mutablecopy。
ARC:
在MRC下,我们通过retain、release、autorelease来完成引用计数(内存管理),在ARC下如何记述如何管理呢?
所有权限修饰符:
__strong:id和对象类型默认的所有权限修饰符。使用__strong修饰的变量在超出作用域时(或者手动置nil、被赋值等),对象被废弃。即随着强引用的失效,引用的对象随之释放。
但是__strong还不够“strong”,使用__strong时会出现相互强引用的情况:
相互强引用是内存泄漏的一种情况,对象得不到应有的释放。这里详细说一下相互强引用:
首先要理解这个过程:比如说有个类A,它的一个实例a,类A中有一个类B的成员变量bb,a和a的成员变量bb都被初始化了,当a被废弃时会怎么样? a超出作用域时,a之前所引用的对象被释放,然后变量bb被废弃,bb之前所引用的对象被释放。再来说这种情况,有个类A,它的一个实例a,类A中有一个类B的成员变量bb;有个类B,它的一个实例b,类B中有一个类A的成员变量aa,a和b初始化之后,分别将a、b赋给aa、bb。当a和b超出作用域之后,a所引用的对象没有被释放,因为aa还在引用它,b所引用的对象没有被释放,因为bb还在引用它。
__weak提供弱引用,__weak不持有对象,也就是说对象失去强引用之后无论有多少弱引用都将被释放。弱引用还会自动失效处于nil状态。
__unsafe_unretained是不安全的所有权修饰符,使用该修饰符修饰的变量不属于编译器的内存管理对象,当其引用的对象被释放后不会自动置nil。C语言的结构体中,不能含有OC对象,因为C语言没有方法来管理结构体成员的生存周期,要想加入OC对可以强制转换为void *或使用__unsafe_unretained修饰符。
__autoreleasing修饰符:
这个修饰符是与MRC下的autorelease方法对应的修饰符。在ARC下不能使用autorelease方法,也不能使用NSAutoreleasePool类,但是可以使用@autoreleasepool { } 块来替代NSAutoreleasePool的作用、使用__autoreleasing修饰符代替调用autorelease方法。
以下几种情况非显示的使用__autoreleaseing:
不是 以alloc、new、copy、mutableCopy开头的方法 的返回值的对象 默认注册到autoreleasePool中。
在访问__weak修饰的变量时,该变量默认被注册到autoreleasePool中,因为__weak只持有对象的弱引用,在访问的过程中很可能对象被废弃了。这样可以保证在@autoreleasePool{}块结束前对象都不会被释放。
init方法返回的对象不注册到autoreleasePool。(以init开头的方法必须是实例方法,并且必须返回id类型或者该方法所在类(也可以是其父类或子类)类型的对象。(-(void)initialiaze方法除外)。
要注意只有相同所有权修饰符相同的变量才可以赋值,否则编译报错,如果没有报错则说明编译器自行帮你转化了。
最后给出属性与所有权限修饰符的关系:
assign__unsafe_unretained
copy__strong
retain__strong
strong__strong
unsafe_unretained __unsafe_unretained
weak__weak