基础程序员iOS技术交流收藏

<<iOS 与OS X多线程和内存管理>>

2017-01-24  本文已影响2162人  神经骚栋
注:本文为笔记形式,所以很多都是摘抄的.<<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版的下载传送门,各位看官可以自行去参考查看.

<<iOS 与OS X多线程和内存管理>>的pdf版传送门🚪

上一篇下一篇

猜你喜欢

热点阅读