注:本文为笔记形式,所以很多都是摘抄的.<<iOS 与OS X多线程和内存管理>>书中写的很棒,简单易懂,建议各位看官自己去看看.
ARC和MRC
前一篇主要是MRC环境下的引用计数,这一篇我们主要说一下ARC环境以及所有权修饰符,在Xcode4.2之后,苹果公司开始推出ARC环境,ARC会自动的帮助我们处理"引用计数"的问题,这样让iOS开发人员能够不再关注内存管理这一块,让开发人员更加注重应用的开发.在Xcode切换ARC和MRC如下图所示.
所有权修饰符说明
在ARC环境下,id类型一共有四种所有权修饰符,分别是 __strong
、__weak
、__unsafe_unrerained
、__autoreleasing
.下面我们就分别对这四种所有权修饰符进行说明讲解.
__strong修饰符
__strong
修饰符是id类型和对象类型默认的所有权修饰符,也就是说,所有对象的创建都被默认加上了__strong
修饰符.
//默认是加上了__strong修饰符
id obj = [[NSObject alloc]init];
在ARC环境下,下面代码等同于上面代码.
id __strong obj = [[NSObject alloc]init];
那么在MRC中,__strong
修饰符代表着什么呢?如下代码所示.
{
id __strong obj = [[NSObject alloc]init];
}
上面的代码好像不能很好的表达__strong
修饰符在MRC环境中的所表示的意义.其实在MRC环境中,下面代码等同于上面.其中{}
之间代表着变量的作用域,__strong
修饰符表示对对象的"强引用",持有强引用的变量在超出作用域时被废弃,随着强引用的失效,引用的对象也随之释放.
{
id obj = [[NSObject alloc]init];
[obj release];
}
上面是说的自己生成并且持有的对象,那么对于非自己生成并且持有的对象,如下代码所示.这样对象的所有者和对象的生存周期就不明确了,__strong
有做了什么操作呢?这其中和MRC的手动管理对象的引用计数是一样的,当超出对象的作用域的时候,强引用会失去作用.这时候,会自动的释放自己持有的对象,如果对象的所有者都不存在了,也就是说对象的引用计数为0的时候,会废弃对象.
{
id __strong obj = [NSMutableArray array];
}
那么,有了强引用__strong
之后,为什么会出现__weak
弱引用呢?这就是我们在平常开发过程中经常遇到的一个问题,循环引用,下面我们就在__weak
这个修饰符中说说循环引用的出现以及解决方案.
__weak修饰符
循环引用一般分为两种情况,一种是两个对象之间相互持有;另外一种则是对象本身持有自己.我们先看一下两个对象之间相互持有,比如我们创建两个数组,然后相互添加对方,如下所示,这样就会造成内存泄漏问题.
{
NSMutableArray *firstArray=[[NSMutableArray alloc]init];
NSMutableArray *secondArray=[[NSMutableArray alloc]init];
[firstArray addObject:secondArray];
[secondArray addObject:firstArray];
}
对于内存泄漏的检测,各位看官可以查看iOS开发之内存泄漏检测工具-Leaks,我们可以使用Leaks工具检测内存泄漏.那么到底是怎么造成内存泄漏的呢?
首先,{}
之间仍然代表着变量的作用域,我们在作用域创建两个数组,其中firstArray持有数组对象A,secondArray持有数组对象B,如下所示.
{
NSMutableArray *firstArray=[[NSMutableArray alloc]init];//数组对象A
NSMutableArray *secondArray=[[NSMutableArray alloc]init];//数组对象B
}
问题出现假两者的互相添加,现在通过firstArray把数组对象B添加到数组对象A中,通过secondArray把数组对象A添加到数组对象B中,这样数组对象A和B这两者就相互持有了.
[firstArray addObject:secondArray];
[secondArray addObject:firstArray];
上面的两大部分看似是没有任何问题,但是问题出现在超出作用域释放对象的时候,首先,因为firstArray持有数组对象A的强引用,secondArray持有数组对象B的强引用,两者都会自动释放,此时,数组对象A和B相互持有所以不能释放.可以理解为造成了一个死循环(A中有B,B中有A,比如释放A,那么需要把A中的B也释放掉,B中又存在着A,那么又要接着释放A,如此无限循环下去.造成不能释放).
还有一种内存泄漏的情况就是对象本身持有对象本身,情况和上面情况类似.代码如下.
{
NSMutableArray *testArray=[[NSMutableArray alloc]init];
[testArray addObject:testArray];
}
那么,OC是如何解决相互引用的,这就需要使用到__weak
修饰符了,如下代码所示.
__weak NSMutableArray *testArray=[[NSMutableArray alloc]init];
但是这样是出现警告的,原因就是__weak
修饰符是为了不以自己持有的状态来保存自己生成并且持有的对象,生成对象会被立即释放掉.这时候testArray实际上是不持有数组对象的.
这样我们就可以使用__weak
修饰符来解决循环引用问题了.但是呢,根据书中所讲,__weak
修饰符只能用于iOS 5以上及OS X Lion版本的程序,那么在此之前用的就是我们下一个模块要说的__unsafe_unrerained
修饰符.
__unsafe_unrerained修饰符
相比于__weak
和__strong
这两个我们常用的修饰符, __unsafe_unrerained
修饰符确实已经不多见了.现在Xcode都已经是8.0+,所以,我们需要对__unsafe_unrerained
修饰符有个大体的了解即可.__unsafe_unrerained
修饰符是不安全的所有权修饰符,它的作用和__weak
修饰符的作用类似.
__autoreleasing修饰符
我们知道在ARC环境下我们不能使用autorelease这个方法,也不能使用NSAutoreleasePool
这个类,关于autorelease的使用方法请查看<<iOS 与OS X多线程和内存管理>>笔记:MRC与引用计数中的autorelease模块.使用方法如下所示.
@autoreleasepool {
__autoreleasing id obj = [[NSMutableArray alloc]init];
}
上面的代码等同于在MRC环境下的如下代码.
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
id obj = [[NSMutableArray alloc]init];
[obj autorelease];
[pool drain];
那么 __autoreleasing
修饰符在我们实际开发过程中是怎么使用的呢?其中,被__weak修饰过的对象实际上必定要访问注册到autoreleasepool的对象.这是为什么呢?我们知道被__weak
修饰过的对象只持有对象的弱引用,而在访问引用对象的过程中,该对象有可能被废弃.如果把要访问的对象注册到autoreleasepool中,那么@autoreleasepool块结束之前,我们都能访问到该对象.所以被__weak修饰过的对象实际上必定要访问注册到autoreleasepool的对象.示例代码如下所示.
@autoreleasepool {
id obj = [[NSObject alloc]init];
__weak id obj1 = obj;
__autoreleasing id tmp = obj1;
}
尾声
笔记将继续进行下去,欢迎大家一起来讨论<<iOS 与OS X多线程和内存管理>>相关问题,如果有任何问题,欢迎联系骚栋,谢谢.最后还是<<iOS 与OS X多线程和内存管理>>的pdf版的下载传送门,各位看官可以自行去参考查看.