前言

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. 最后一个非零数字(POJ 1604、POJ 1150、POJ 3406)

    POJ中有些问题给出了一个长数字序列(即序列中的数字非常多),这个长数字序列的生成有一定的规律,要求求出这个长数字序列中某个位上的数字是多少.这种问题通过分析,找出规律就容易解决. 例如,N!是一个非 ...

  2. python 双冒号

    Python序列切片地址可以写为[开始:结束:步长],其中的开始和结束可以省略 1.range(n)生成[0,n)区间整数 range(10) [0,1,2,3,4,5,6,7,8,9] 2.开始st ...

  3. 01Oracle Database

    Oracle Database Oracle Database又名Oracle RDBMS简称Oracle是甲骨文公司的一款关系数据库管理系统. Oracle Website.

  4. S​Q​L​_​S​e​r​v​e​r​_​2​0​0​8​定​期​自​动​备​份​详​细​图​解

    S​Q​L​_​S​e​r​v​e​r​_​2​0​0​8​定​期​自​动​备​份​详​细​图​解 设置自动数据库的定期备份计划. http://wenku.baidu.com/link?url=Tu ...

  5. vue开发 - 根据vue-router的meta动态设置html里标签的内容

    路由文件 :router/index.js import Vue from 'vue'import Router from 'vue-router'import index '@/view/index ...

  6. 使用webpack搭建react项目 webpack-react-project

    webpack-react-project 使用webpack搭建react项目 webpack搭建react项目 github源码 具体配置信息参照package.json和webpack.conf ...

  7. glibc库函数,系统调用API

    glibc封装了大部分系统API,我们一般都是使用glibc封装的接口进行系统调用,碰到一些没有封装的接口,可以通过这个 函数syscall 进行系统调用.   /* Invoke `system c ...

  8. 实现基于pam认证的vsftpd

    1 需求 使用指定虚拟用户Allen与Barry登录ftp,认证的源是mysql服务器: Allen可以上传文件,Barry不可以上传文件: 2 环境 [root@centos7 ~]# cat /e ...

  9. 集训第四周(高效算法设计)J题 (中途相遇法)

    Description   The SUM problem can be formulated as follows: given four lists A, B, C, D<tex2html_ ...

  10. Django——分页功能Paginator

    Django分页功能----Paginator Paginator所需参数: Paginator(object_list,per_page) Paginator常用属性: per_page: 每页显示 ...