ios-ARC、MRC、自动释放池

来源:互联网 发布:知乎如何修改关注话题 编辑:程序博客网 时间:2024/05/20 07:16

在网上看了几篇文章也想记录记录方便以后的查看吧。以下记录来自于网络。

在IOS程序运行过程中,会创建无数个池子,这些池子都是以栈结构(先进后出)存在的。
当一个对象调用autorelease时,会将这个对象放到位于栈顶的释放池中

@autoreleasepool是自动释放池,让我们更自由的管理内存,在MRC环境下如果有alloc,new,copy如果想延迟释放,我们都应该在自动释放池中加上autorelease来延迟释放

当我们手动创建了一个@autoreleasepool,里面创建了很多临时变量,当@autoreleasepool结束时,里面的内存就会回收

ARC时代,系统自动管理自己的autoreleasepool,runloop就是iOS中的消息循环机制,当一个runloop结束时系统才会一次性清理掉被autorelease处理过的对象,其实本质上说是在本次runloop迭代结束时清理掉被本次迭代期间被放到autorelease pool中的对象的。至于何时runloop结束并没有固定的duration。

关于RunLoop,每个线程都有一个RunLoop对象,但是只有主线程的RunLoop是开启的。子线程中的RunLoop默认是不被创建的,在子线程中当我们调用NSRunLoop *runloop = [NSRunLoop currentRunLoop];获取RunLoop对象的时候,就会创建RunLoop一个线程可以开启多个RunLoop,只不过都是嵌套在最大的RunLoop中。

在没有手加Autorelease Pool的情况下,Autorelease对象是在当前的runloop迭代结束时释放的,而它能够释放的原因是系统在每个runloop迭代中都加入了自动释放池Push和Pop。系统就是通过@autoreleasepool {}这种方式来为我们创建自动释放池的,一个线程对应一个runloop,系统会为每一个runloop隐式的创建一个自动释放池,所有的autoreleasePool构成一个栈式结构,在每个runloop结束时,当前栈顶的autoreleasePool会被销毁,而且会对其中的每一个对象做一次release(严格来说,是你对这个对象做了几次autorelease就会做几次release,不一定是一次),特别指出,使用容器的block版本的枚举器的时候,系统会自动添加一个autoreleasePool
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
// 这里被一个局部@autoreleasepool包围着
}];

每个线程包括主线程都保持它自己的自动释放池的栈,当新的池被创建,它们被添加到栈的顶部,放池被销毁,它们会从栈移除,自动释放的对象被放在当前线程的最顶部的自动释放池中,当线程被终止,它会自动清空所有与自己关联的自动释放池。

如果使用Automatic Reference Counting(ARC).不能直接使用autorelease pools,而是使用@autoreleasepool{},
@autoreleasepool{}比直接使用NSAutoreleasePool效率高。不使用ARC的时候也可以使用

什么时候用@autoreleasepool

 写基于命令行的的程序时,就是没有UI框架,如AppKit等Cocoa框架时。
    写循环,循环里面包含了大量临时创建的对象。(本文的例子)
    创建了新的线程。(非Cocoa程序创建线程时才需要)
    长时间在后台运行的任务。

在MRC中可以手动创建一个自动释放出池,用NSAutoreleasePool,而在ARC中不可以用NSAutoreleasePool,但是可以用@autoreleasepool来创建。autoreleasePool是按照线程一一对应的,

我们在MRC中如果手动的创建了一个自动释放池的话比如说NSAutoreleasePool *pool = [[ NSAutoreleasePool alloc]init ];//创建一个自动释放池 我们手动释放自动释放池的时候我们最好用[pool release];在这两行代码直接我们可以进行我们要进行的操作,例如加上这句NSArray * array = [[[NSArray alloc] init] autorelease];需要人为调用autorelease方法加入,就是说是在自动释放池销毁的时候会release这个对象。

相信在这个.m文件中大家一定都见过

int main(int argc, char *argv[]){    @autoreleasepool {        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));       //将所有的事件、消息全部交给了 UIApplication 来处理,%zi是输出无符号型    }}
如果不写这个块的话,那么由UIApplicationMain函数所自动释放的那些对象,就没有自动释放池可以容纳了,于是系统会发出警告信息,所以说,这个池可以理解成最外围捕捉全部自动释放对象所用的池,会在程序退出时自动释放。可以把autorelease pool理解成一个类似父类与子类的关系,main()创建了父类,每个Runloop自动生成的或者开发者自定义的autorelease pool都会成为该父类的子类。当父类被释放的时候,没有被释放的子类也会被释放,这样所有子类中的对象也会收到release消息。

做多线程开发时,需要在线程调度方法中手动添加自动释放池,尤其是当执行循环的时候,如果循环内部有使用类的快速创建方法创建的对象, 一定要将循环体放到自动释放池中。 

关于NSAutoreleasePool

NSAutoreleasePool实际上是个对象引用计数自动处理器,在官方文档中被称为是一个类。

NSAutoreleasePool可以同时有多个,它的组织是个栈,总是存在一个栈顶pool,也就是当前pool,每创建一个pool,就往栈里压一个,改变当前pool为新建的pool,然后,每次给pool发送release消息,就弹出栈顶的pool,改当前pool为栈里的下一个 pool。

在每个OC对象都有自己的引用计数器,内部都专门4个字节的存储空间来存储引用计数器。其实引用计数器的作用就是用来判断对象要不要被回收。对象刚被创建时,默认计数器值为1,当计数器的值变为0时,则对象销毁,其占用的内存被系统回收。

总结:个人的理解的话,可能是这样子的,在MRC和ARC中其实每个Runloop都会自动创建一个自动释放池,然后在ARC中我们不需要retain和release和autorelease,因为编译器会自动的帮我们生成,而在MRC中的话,只要还有人在使用某个对象,那么这个对象就不会被回收;只要你想使用这个对象,那么就应该让这个对象的引用计数器+1;当你不想使用这个对象时,应该让对象的引用计数器-1;如果你通过alloc,new,copy来创建了一个对象,那么你就必须调用release或者autorelease方法。只要你用了retain生成一个对象,你就要release它。当对象被销毁时,系统会自动向对象发送一条dealloc消息,一般会重写dealloc方法,在这里释放相关的资源,dealloc就像是对象的“临终遗言”。一旦重写了dealloc方法就必须调用[super dealloc],并且放在代码块的最后调用,不能直接调用dealloc方法。系统自带的方法中,如果不包含alloc new copy等,则这些方法返回的对象都是autorelease的,如[NSDate date];

MRC下的内存管理规范操作

基本数据类型

 -(void)setAge:(int)age{           _age=age;       }
OC对象类型set方法的规范,@property(retain)Car *car有了这个我们在MRC环境下就可以不用写下面的set代码了,系统会自动的帮我们生成下面的,先release旧值,在retain新值。
-(void)setCar:(Car *)car{ //1.先判断是不是新传进来的对象 If(car!=_car){ //2 对旧对象做一次release [_car release];//若没有旧对象,则没有影响 //3.对新对象做一次retain _car=[car retain]; } }
dealloc方法的代码规范

 -(void)dealloc{     对当前所拥有的的其他对象做一次release操作  [_car release];  //self.car=nil;也是可以的,因为这里执行的是car的set方法 [super dealloc];   }
get 方法的代码规范

-(Car *)car{    return _car;    }}
ARC中的规则
Strong:相当于原来的retain(适用于OC对象类型),成员变量是强指针
Weak:相当于原来的assign,(适用于oc对象类型),成员变量是弱指针
Assign:适用于非OC对象类型(基础类型)

持有者就是指向对象的指针,如果是strong修饰的,即是对象的持有者,如果是weak属性的,则不是持有者。

ARC机制的使用规则

(1)不能调用dealloc,不能重写和调用retain,release,retainCount 和autorelease,dealloc虽然能够重写,但是不能调用[super dealloc]之类的方法,CoreFoundation框架由于非从属cocoa框架,所以CFRetain和CFRelease仍然正常使用。

    (2)不能使用NSAllocateObjec或NSDeallocateObject函数来创建对象

    (3)不能在C语言的结构体中使用对象指针,同时建议用object-c的类来管理数据而不是结构体

    (4)不得使用NSAutoreleasePool对象。ARC中,全部使用@autorelease关键字代替,且比NSAutoreleasePool更高效

    (5)不得使用内存Zone,那些牵涉NSZone的方法都不得使用。

    (6)不得对一个属性变量的取值方法命名以new开头

    (7)outlet均用weak关键字修饰,除非他是xib中最顶部的界面元素,则需要strong。

    (8)Core Foundation不适合ARC,该创建的仍创建,该释放的仍释放。

什么情况下需要创建自动释放池?
其实自动释放池存在的意义是为了延迟释放一些对象,延迟向对象发送release消息。在实际的开发中,有两种情况是需要手动创建自动释放池的。
1、在多线程中,因为子线程中可能会使用便利构造器等方法来创建对象,那么这些对象的释放只能放在自动释放池中,此时需要在子线程中添加自动释放池。
主线程已经添加过自动释放池,在main函数里面。
2、就是如果一段代码里面(比如for循环)大量使用便利构造器创建对象,也需要手动添加自动释放池。

什么是便利构造器?

便利构造器在初始化方法的基础上前进了一⼩小步。封装了对象创建过程。
便利构造器是“+”方法,返回本类型的实例,方法名以类名开头,其实一些类中的静态方法也是便利构造器。
可以有0到多个参数。内部实现:封装了alloc和初始化方法。使用起来更加简洁。

比如说

-(Person *)initWithAge: (int)age andWithName: (NSString *)name; - (Person *)initWithAge: (int)age andWithName: (NSString *)name{    _age = age;    _name = name;}+ (Person *)initWithAge: (int)age andWithName: (NSString *)name;//这个类方法在返回Person对象时,就可以给_age和_name赋值,这个就是便利构造器+ (Person *)initWithAge: (int)age andWithName: (NSString *)name{    return [[Person alloc] initWithAge:age andWithName:name];} Person *p  = [Person initWithAge: 10 andWithName: @"jason"];// 在init时就赋值了age和name属性


原创粉丝点击