前言

AutoreleasePool自己主动释放池,对于自己主动释放对象的作用怎样?

释放池中的自己主动释放对象什么时候会被释放?

MRC环境下

场景1

NSString *string_var_ = nil;
- (void)viewDidLoad {
[super viewDidLoad];
NSString *string = [NSString stringWithFormat:@"bluefish"];
string_var_ = string; NSLog(@"viewDidLoad--string: %@", string);
} - (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(@"viewWillAppear--string: %@", string_var_);
} - (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(@"viewDidAppear--string: %@", string_var_);
}

控制台打印:

viewDidLoad--string: bluefish
viewWillAppear--string: bluefish

然后,NSLog(@"viewDidAppear--string: %@", string_var_);

野指针报错。

当中,NSString *string = [NSString stringWithFormat:@"bluefish"];是自己主动释放对象,引用计数为1,string对其引用,在viewDidLoad和viewWillAppear返回时,string对象还没有被release,到viewDidAppear时被release了。也就是说string对象在viewWillAppear和viewDidAppear之间被线程的自己主动释放池release。

那假设string是非自己主动释放的呢。

NSString *string = [[NSString alloc] initWithFormat:@"%@",@"bluefish"];

则三个方法都会打印,但由于string一直没有显式调用relase方法,造成string说引用的[[NSString alloc] initWithFormat:@"%@",@"bluefish"]一直没释放。不符合手动内存管理原则。

场景 2

假设显式使用autoreleasepool自己主动释放池,代码:

- (void)viewDidLoad {
@autoreleasepool {
NSString *string = [NSString stringWithFormat:@"bluefish"];
string_var_ = string;
}
NSLog(@"viewDidLoad--string: %@", string_var_);
}

运行后,NSLog(@"viewDidLoad--string: %@", string_var_);野指针报错。

说明。autorelease创建时(引用计数为1)。会自己主动增加到autoreleasepool{}自己主动释放池里。结束时,会把里面的自己主动释放对象作一次release(引用计数-1),所以string_var_成了野指针。而局部变量string作用域结束被销毁(出栈)。因此log打印时。string_var_野指针报错。

那autoreleasepool里的非autorelease对象会不会被释放呢,我们试试:

@autoreleasepool {
NSString *string = [[NSString alloc] initWithFormat:@"%@",@"bluefish"];
string_var_ = string;
}

结果是三个方法都打印出来了,说明。非autorelease对象不会被增加autoreleasepool。

其情况就跟 “场景1” 一样,string一直得不到释放。

场景3

假设显式使用autoreleasepool自己主动释放池,并且将string定义在autoreleasepool{}外会怎么样,例如以下

- (void)viewDidLoad {
[super viewDidLoad]; NSString *string = nil;
@autoreleasepool {
string = [NSString stringWithFormat:@"bluefish"];
string_var_ = string;
} NSLog(@"viewDidLoad--string: %@", string_var_);
}

运行后,情况跟“场景2”一样,无法成功打印。野指针报错,由于[NSString stringWithFormat:@"bluefish"]为自己主动释放对象,string对其做引用。一旦出了autoreleasepool{},string所指向的[NSString stringWithFormat:@"bluefish"]便会被释放掉。string和string_var_都成了野指针。

那假设换成[[NSString alloc] initWithFormat:@"%@",@"bluefish"]会怎么。答案是跟“场景2”一样,对象引用一直有效。由于一直没有release。

小结:在普通情况下,创建的autorelease对象会被增加到离自己近期的释放池,假设没显式使用autoreleasepool{},则增加到当前线程的释放池中。一旦释放池结束,里面的对象都会做一次release。

注意即使显式地使用autoreleasepool{},里面的非自己主动释放对象也要手动release,否则对象一直不会释放。

以上是MRC(手动内存管理)下。自己主动释放池autoreleasepool对对象的处理差异。

但在ARC下会怎样呢。

ARC环境下

场景1

这里我们使用_weak修饰外包变量string_weak。依据arc的规则。string_weak_不正确他引用的对象做持有。不会影响引用的对象的释放,当所应用对象被释放时,string_weak_会自己主动被设为nil。所以这里用弱引用变量string_weak_来观察对象的释放,以下看代码:

__weak NSString *string_weak_ = nil;

- (void)viewDidLoad {
[super viewDidLoad]; // 场景 1
NSString *string = [NSString stringWithFormat:@"bluefish"];
string_weak_ = string; NSLog(@"viewDidLoad--string: %@", string_weak_);
} - (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(@"viewWillAppear--string: %@", string_weak_);
} - (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(@"viewDidAppear--string: %@", string_weak_);
}

运行后控制台输入:

viewDidLoad--string: bluefish
viewWillAppear--string: bluefish
viewDidAppear--string: (null)

从打印接口可看string对象在viewWillAppear和viewDidAppear之间被释放。这个释放时机与MRC下是一样的,仅仅只是MRC下变成野指针报错而已。但理解方式就不一样了。

[NSString stringWithFormat:@"bluefish"]创建时,被自己主动增加所在线程的自己主动释放池中。这样,它的释放就交给了释放池去管理了。然后,string对其做引用,由于arc下。默认是_strong,所以string对其做了一次强引用。而当string出了viewDidLoad方法。局部变量string就会被销毁。这时,string对[NSString stringWithFormat:@"bluefish"]的强引用就消失了,可是由于[NSString stringWithFormat:@"bluefish"]还在自己主动释放池中未被释放,所以string_weak还能够引用到它,直到当前线程自己主动释放池释放[NSString stringWithFormat:@"bluefish"]

那么假设是非自己主动释放对象呢:

NSString *string = [[NSString alloc] initWithFormat:@"%@",@"bluefish"];

运行打印结果:

 viewDidLoad--string: bluefish
viewWillAppear--string: (null)
viewDidAppear--string: (null)

结果显示[[NSString alloc] initWithFormat:@"%@",@"bluefish"]一离开viewDidLoad方法。就被释放了。

由于,[[NSString alloc] initWithFormat:@"%@",@"bluefish"]为非自己主动释放对象,创建时。不会被增加自己主动释放池中,所以它的释放是在arc机制下的。当强引用对象string离开作用域时。被销毁,[[NSString alloc] initWithFormat:@"%@",@"bluefish"]失去全部的强引用,没有不论什么一个强引用对象指向它,因此arc对其进行了release。

场景2

显式使用autoreleasepool自己主动释放池。代码:

- (void)viewDidLoad {
[super viewDidLoad]; @autoreleasepool {
NSString *string = [NSString stringWithFormat:@"bluefish"];
string_weak_ = string;
}
NSLog(@"viewDidLoad--string: %@", string_weak_);
}

运行打印结果:

 viewDidLoad--string: (null)
viewWillAppear--string: (null)
viewDidAppear--string: (null)

从结果看。[NSString stringWithFormat:@"bluefish"]一出autoreleasepool{}就被释放了。

分析一下,

跟“场景1”一样,[NSString stringWithFormat:@"bluefish"]创建时。被自己主动增加autoreleasepool{}自己主动释放池中,这样,它的释放就交给了释放池去管理了。

然后,string对其做强引用,并且string是在autoreleasepool{}中定义的,当autoreleasepool{}结束时,string被销毁。强引用消失。而这时,autoreleasepool{}自己主动释放池也结束了。因此[NSString stringWithFormat:@"bluefish"]也被释放掉。

那假设换成非自己主动释放对象呢。

 @autoreleasepool {
NSString *string = [[NSString alloc] initWithFormat:@"%@",@"bluefish"];
string_weak_ = string;
}

打印结果还是一样,why?以下分析,

由于[[NSString alloc] initWithFormat:@"%@",@"bluefish"]非自己主动释放,所以他不会增加到autoreleasepool,而是靠arc来管理内存,string对其作强引用。

但autoreleasepool{}结束时,局部变量string销毁。而arc下,[[NSString alloc] initWithFormat:@"%@",@"bluefish"]一旦失去了不论什么强引用,也会被释放。所以。最后一出autoreleasepool{}就都被释放了。

PS:能够使用lldb指令 watchpoint set v string_weak_ 设置观察点。观察 string_weak_ 变量的值的变化。

先设置一个断点,然后设置watchpoint set v string_weak_ 。继续运行程序。假设string_weak_改变(变为nil),就会被检測到。

watchpoint set v string_weak_
Watchpoint created: Watchpoint 1: addr = 0x000978e0 size = 4 state = enabled type = w
declare @ '/Users/Bluefish/Documents/textPro/AutoreleasePoolTest/AutoreleasePoolTest/ViewController.m:15'
watchpoint spec = 'string_weak_'
new value: 0x7a160340 Watchpoint 1 hit:
old value: 0x7a160340
new value: 0x00000000

场景3

显式使用autoreleasepool自己主动释放池,并且将string定义在autoreleasepool{}外,例如以下

- (void)viewDidLoad {
[super viewDidLoad]; NSString *string = nil;
@autoreleasepool {
string = [NSString stringWithFormat:@"bluefish"];
string_weak_ = string;
} NSLog(@"viewDidLoad--string: %@", string_weak_);
}

运行结果:

viewDidLoad--string: bluefish
viewWillAppear--string: (null)
viewDidAppear--string: (null)

你可能会奇怪为什么出了autoreleasepool{}后,还是能够打印出来,[NSString stringWithFormat:@"bluefish"]没被释放掉。

以下分析。

[NSString stringWithFormat:@"bluefish"]创建时,被增加autoreleasepool释放池。string对其强引用,当autoreleasepool{}结束时。[NSString stringWithFormat:@"bluefish"]被释放,可是,string还在其作用域内,因此还继续对[NSString stringWithFormat:@"bluefish"]做强引用,所以不会被释放。而直到viewDidLoad{}结束,string被销毁,强引用消失,在arc下。失去了不论什么强引用的[NSString stringWithFormat:@"bluefish"]终于被释放掉。

那假设是非自己主动释放对象呢。例如以下:

NSString *string = nil;
@autoreleasepool {
string = [[NSString alloc] initWithFormat:@"%@",@"bluefish"];
string_weak_ = string;
}

运行打印结果也是一样。在这里autoreleasepool{}自己主动释放池事实上起不到什么实际作用。以下分析,

[[NSString alloc] initWithFormat:@"%@",@"bluefish"]创建时。由于其不是自己主动释放对象,所以并不会增加到autoreleasepool的自己主动释放池中,也就是他的内存管理还是由arc来处理。string对其强引用。当autoreleasepool{}结束时,释放池中没有可释放的对象,而string也还在其作用域内。所以,autoreleasepool{}什么也没做,到viewDidLoad{}结束时,string销毁,[[NSString alloc] initWithFormat:@"%@",@"bluefish"]失去不论什么强引用,终于都被释放。

小结:在arc环境下。autoreleasepool{}相同也仅仅对自己主动释放对象做管理,当autoreleasepool{}结束时,自己主动释放池中的对象。

但由于arc机制的存在,autoreleasepool{}的功能事实上能够当成是被弱化的,它的作用很多其它的是用于在arc环境下,控制自己主动对象的释放时机,如“场景2”。

总结

仅仅有自己主动释放对象会被增加离它近期的自己主动释放池中,自己主动释放池结束时,池中的对象都会被释放一次。

局部变量。在其作用域结束后,就会被销毁(出栈)。

普通情况下。在程序中的自己主动释放对象(非显式使用alloc new copy或者MRC下使用autorelease)会被增加到当前线程的自己主动释放池中(每一个线程都会有一个默认的自己主动释放池)。当这线程结束时,其自己主动释放池中的对象被销毁。參考“场景1”。所以。当你没显式使用autoreleasepool{}的时候,程序中的自己主动释放对象依旧是在一个隐式的自己主动释放池中(你的程序都是运行中各个线程中的。包含主线程)。

在MRC下。是通过引用计数的概念来管理内存。一个对象一旦被创建其引用计数即为1。创建后你能够使用其它普通的对象指针变量来指向它,跟arc不同是指向它不会改变这个对象的引用计数,除非向对象发送retain或者copy(浅拷贝)计数+1,创建出来的对象,假设是通过alloc new copy创建出来的。除了自己主动释放对象最后都要向对象发送release方法。

所以,最后,仅仅要记得一条准则,在MRC下。在autoreleasepool{}中创建的非自己主动释放对象都要调用release方法。

而autoreleasepool{}创建的自己主动释放对象,一旦autoreleasepool结束就会被release一次。无论其之前被哪个对象引用过。

在ARC下,内存管理以对象强弱引用来处理,一个对象一旦失去全部强引用,就会被销毁。其弱引用会被自己主动设为nil。

在ARC下。创建出来的对象能够视为两种不同的内存处理模式。

一种是非自己主动释放对象,其内存管理由arc来处理。

一种是自己主动释放对象,其内存管理由自己主动释放池来出来。

同理。在autoreleasepool{}中创建的非自己主动对象由arc来处理,自己主动释放对象由当前autoreleasepool来管理。但由于arc的环境下,无论在autoreleasepool{}中创建的非自己主动释放对象还是自己主动释放对象。最后都会被释放一次(由于autoreleasepool{}中的非自己主动释放对象会在该作用域范围结束时被arc释放一次)。

而假设在autoreleasepool{}结束时。有作用域在该autoreleasepool{}外的变量对里面创建的对象做强引用时,该对象继续保留。

AutoreleasePool 分析的更多相关文章

  1. AutoReleasePool 和 ARC 以及Garbage Collection

    AutoReleasePool autoreleasepool并不是总是被auto 创建,然后自动维护应用创建的对象. 自动创建的情况如下: 1. 使用NSThread的detachNewThread ...

  2. Cocos2d-X3.0 刨根问底(四)----- 内存管理源码分析

    本系列文章发表以来得到了很多朋友的关注,小鱼在这里谢谢大家对我的支持,我会继续努力的,最近更新慢了一点,因为我老婆流产了抽了很多时间来照顾她希望大家谅解,并在此预祝我老婆早日康复. 上一篇,我们完整的 ...

  3. 【Cocos2d-x 3.x】内存管理机制与源码分析

    侯捷先生说过这么一句话 :  源码之前,了无秘密. 要了解Cocos2d-x的内存管理机制,就得阅读源码. 接触Cocos2d-x时, Cocos2d-x的最新版本已经到了3.2的时代,在学习Coco ...

  4. Cocos2d-X3.0 刨根问底(五)----- Node类及显示对象列表源码分析

    上一章 我们分析了Cocos2d-x的内存管理,主要解剖了 Ref.PoolManager.AutoreleasePool这三个类,了解了对象是如何自动释放的机制.之前有一个类 Node经常出现在各种 ...

  5. Cocos2d-X3.0 刨根问底(三)----- Director类源码分析

    上一章我们完整的跟了一遍HelloWorld的源码,了解了Cocos2d-x的启动流程.其中Director这个类贯穿了整个Application程序,这章随小鱼一起把这个类分析透彻. 小鱼的阅读源码 ...

  6. set方法内存分析

    // //  main.m //  04-set方法的内存管理分析 // //  Created by apple on 14-3-17. // // #import <Foundation/F ...

  7. Runloop与autoreleasePool联系

    autoreleasePool自动释放池,ARC模式下,苹果会自动进行内存管理,不需要我们手动去管理内存.这对于苹果开发者来说,省去了很多事情,不用再每天为了内存管理浪费掉宝贵的开发时间.大家都知道, ...

  8. 【转】SDWebImage实现分析

    该博文来自南峰子的技术博客,文章从下载和缓存俩个大的组件分析到里面一些核心方法的实现,条理清晰,相对于一些一上来就通篇分析实现思路的技术文章, 这篇的讲解思路明确,框架架构也讲的比较清楚.看完这篇再去 ...

  9. iOS 崩溃分析

    崩溃统计分析,在APP中是非常常见一种优化APP,发现APP的BUG的方式. 1.异常处理 可通过try catch 方式处理,如果发生异常,会走catch ,最终走fianlly.对一些我们不想他崩 ...

随机推荐

  1. zTree 点击文字 勾选check

    callback: { onClick:function(event, treeId, treeNode){ console.info("onClick") var treeObj ...

  2. Maven的pom报错的解决方法

    如果在MyEclipse里面导入项目,导入不了,如下图 接下来可以点击Import Maven Projects里的Action那一行Resolve Later. 点击Do Not Execute(a ...

  3. ORA-39126: Worker unexpected fatal error in KUPW$WORKER.PUT_DDLS

    末尾加上EXCLUDE=STATISTICS /home/opt/oracle/11g/bin/expdp user/password directory=backup2 network_link=d ...

  4. springboot Tomcat connector configured to listen on port 8081 failed to start.

    启动报 Tomcat connector configured to listen on port 8081 failed to start.   The port may already be in ...

  5. 第3节 mapreduce高级:10、11、分组求取topN

    只要修改OrderReducer.java的reduce方法,修改为: int i = 0;for(NullWritable nullWritable:values){ if(i>=2) bre ...

  6. MySql索引笔记

    MySQL 索引是什么 MySQL索引的建立对于MySQL的高效运行是很重要的,索引可以大大提高MySQL的检索速度. 打个比方,如果合理的设计且使用索引的MySQL是一辆兰博基尼的话,那么没有设计和 ...

  7. PHP程序员必须知道的两种日志

    前言 作为一名程序员,比码代码还重要那么一点点的东西就是日志的分析和查询.下面列出常见日志及设置方法. php-fpm 慢日志 php慢日志需要在php-fpm.conf设置,如果使用源码包安装默认请 ...

  8. Bootstrap-Table 总结

    Bootstrap-Table 总结 jQuery Java Bootstrap-Table JS文件 传参:直接将需要的参数置于 queryParams 方法中,例如 line:formData注意 ...

  9. [Android] java代码无错误,但跳转失败

    今天在调代码的时候,出现了这样的问题,我晕了半天,才找到解决办法. 查看日志发现:Initialize Binary Program Cache: Load Failed 从来没见过这种问题,Java ...

  10. LeetCode 188. Best Time to Buy and Sell Stock IV (stock problem)

    Say you have an array for which the ith element is the price of a given stock on day i. Design an al ...