@AUTORELEASEPOOL
Swift 在内存管理上使用的是自动引用计数 (ARC) 的一套方法,在 ARC 中虽然不需要手动地调用像是 retain,release 或者是 autorelease 这样的方法来管理引用计数,但是这些方法还是都会被调用的
-- 只不过是编译器在编译时在合适的地方帮我们加入了而已。其中 retain 和 release 都很直接,就是将对象的引用计数加一或者减一。但是autorelease 就比较特殊一些,它会将接受该消息的对象放到一个预先建立的自动释放池
(auto release pool) 中,并在 自动释放池收到 drain 消息时将这些对象的引用计数减一,然后将它们从池子中移除 (这一过程形象地称为“抽干池子”)。
在 app 中,整个主线程其实是跑在一个自动释放池里的,并且在每个主 Runloop 结束时进行 drain操作。这是一种必要的延迟释放的方式,因为我们有时候需要确保在方法内部初始化的生成的对象在被返回后别人还能使用,而不是立即被释放掉。
在 Objective-C 中,建立一个自动释放池的语法很简单,使用 @autoreleasepool 就行了。如果你新建一个 Objective-C 项目,可以看到 main.m 中就有我们刚才说到的整个项目的
autoreleasepool:
int main(int argc, char *argv[]) {
@autoreleasepool {
int retVal = UIApplicationMain(
argc,
argv,
nil,
NSStringFromClass([AppDelegate class]));
return retVal;
}
}
更进一步,其实 @autoreleasepool 在编译时会被展开为 NSAutoreleasePool,并附带 drain方法的调用。
而在 Swift 项目中,因为有了 @UIApplicationMain,我们不再需要 main 文件和 main 函数,所以原来的整个程序的自动释放池就不存在了。即使我们使用 main.swift 来作为程序的入口时,也是不需要自己再添加自动释放池的。
但是在一种情况下我们还是希望自动释放,那就是在面对在一个方法作用域中要生成大量的 autorelease 对象的时候。在 Swift 1.0 时,我们可以写这样的代码:
func loadBigData() {
if let path = NSBundle.mainBundle()
.pathForResource("big", ofType: "jpg") {
for i in 1...10000 {
let data = NSData.dataWithContentsOfFile(
path, options: nil, error: nil)
NSThread.sleepForTimeInterval(0.5)
}
}
}
dataWithContentsOfFile 返回的是 autorelease 的对象,因为我们一直处在循环中,因此它们将一直没有机会被释放。如果数量太多而且数据太大的时候,很容易因为内存不足而崩溃。在 Instruments 下可以看到内存 alloc
的情况:

这显然是一幅很不妙的情景。在面对这种情况的时候,正确的处理方法是在其中加入一个自动释放池,这样我们就可以在循环进行到某个特定的时候施放内存,保证不会因为内存不足而导致应用崩溃。在 Swift 中我们也是能使用 autoreleasepool 的 --
虽然语法上略有不同。相比于原来在 Objective-C 中的关键字,现在它变成了一个接受闭包的方法:
func autoreleasepool(code: () -> ())
利用尾随闭包的写法,很容易就能在 Swift 中加入一个类似的自动释放池了:
func loadBigData() {
if let path = NSBundle.mainBundle()
.pathForResource("big", ofType: "jpg") {
for i in 1...10000 {
autoreleasepool {
let data = NSData.dataWithContentsOfFile(
path, options: nil, error: nil)
NSThread.sleepForTimeInterval(0.5)
}
}
}
}
这样改动以后,内存分配就没有什么忧虑了:

这里我们每一次循环都生成了一个自动释放池,虽然可以保证内存使用达到最小,但是释放过于频繁也会带来潜在的性能忧虑。一个折衷的方法是将循环分隔开加入自动释放池,比如每 10 次循环对应一次自动释放,这样能减少带来的性能损失。
其实对于这个特定的例子,我们并不一定需要加入自动释放。在 Swift 中更提倡的是用初始化方法而不是用像上面那样的类方法来生成对象,而且在 Swift 1.1 中,因为加入了可以返回 nil 的初始化方法,像上面例子中那样的工厂方法都已经从
API 中删除了。今后我们都应该这样写:
let data = NSData(contentsOfFile: path)
使用初始化方法的话,我们就不需要面临自动释放的问题了,每次在超过作用域后,自动内存管理都将为我们处理好内存相关的事情。
@AUTORELEASEPOOL的更多相关文章
- @autoreleasepool在MRC和ARC中的区别
对于@autoreleasepool {} (1)在ARC中会销毁所有在里面创建的对象,即使你用外面的Strong指针指向他 (2)在MRC中如果有外部的强指针指向,不会销毁对象,retainCoun ...
- autoreleasepool自动释放池
示例: @autoreleasepool { ; i[largeNumber; i++) { (因识别问题,该行代码中尖括号改为方括号代替) Person *per = [[Person alloc ...
- AutoReleasePool 和 ARC 以及Garbage Collection
AutoReleasePool autoreleasepool并不是总是被auto 创建,然后自动维护应用创建的对象. 自动创建的情况如下: 1. 使用NSThread的detachNewThread ...
- 深入剖析AutoreleasePool
[深入剖析AutoreleasePool] Objc的AutoreleasePool是一个首尾相连的内存链接,每块大小为1页(32位机上为4kb). 上面可以看到,parent指向父Pool,chil ...
- Objc中2维指针作为输出参数时由ARC及@autoreleasepool引发的血案
先看下面一个例子 #import <UIKit/UIKit.h> #import "AppDelegate.h" @interface Something : NSOb ...
- iOS 非ARC基本内存管理系列 4-autorelease方法和@autoreleasepool
1.autorelease 基本用法 对象执行autorelease方法时会将对象添加到自动释放池中 当自动释放池销毁时自动释放池中所有对象作release操作 对象执行autorelease方法后自 ...
- autoreleasepool的笔记
1.autoreleasepool总是会被问到,放在自动释放池中的对象合适被释放?理解不正确的答案:{}出了大括号.出了作用域等等.个人认为参考答案是,1.在不是手动添加的AutoreleasePoo ...
- iOS基本内存管理:autorelease和autoreleasepool
1.autorelease 基本用法 对象执行autorelease方法时会将对象添加到自动释放池中 当自动释放池销毁时自动释放池中所有对象作release操作 对象执行autorelease方法后自 ...
- cocos2D-x 3.5 引擎解析之--引用计数(Ref),自己主动释放池(PoolManager),自己主动释放池管理器( AutoreleasePool)
#include <CCRef.h> Ref is used for reference count manangement. If a classinherits from Ref. C ...
- Runloop与autoreleasePool联系
autoreleasePool自动释放池,ARC模式下,苹果会自动进行内存管理,不需要我们手动去管理内存.这对于苹果开发者来说,省去了很多事情,不用再每天为了内存管理浪费掉宝贵的开发时间.大家都知道, ...
随机推荐
- 洛谷P3372--线段树代码模板1
如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.求出某区间每一个数的和 输入格式 第一行包含两个整数N.M,分别表示该数列数字的个数和操作的总个数. 第二行包含N个用空格 ...
- 读取普通java web项目下的WEB-INF目录下的配置文件(application.xml,xx.properties等配置文件)
一.在Java web工程WEB-INF下创建weixin.properties属性文件 weixin.properties属性文件里内容如下: 二.创建PropertiesUtils.java测试类 ...
- CIP 协议安全扫盲
- C++——异常处理
前言 大型和十分复杂的程序往往会产生一些很难查找的甚至是无法避免的运行时错误.当发生运行时错误时,不能简单地结束程序运行,而是退回到任务的起点,指出错误,并由用户决定下一步工作.面向对象的异常处理(e ...
- ubuntu16搭建LAMP环境
准备工作: 安装ubuntu16虚拟机,可以正常访问网络 更新为国内源(下载快一些) 1.安装apache sudo apt-get install apache2 然后打开我们的浏览器,访问一下 1 ...
- Spring Aop织入点语法
Aspectj织入点语法: 1.execution(public * *(..)) 任何类的任何返回值的任何方法 2.execution(* set*(..)) 任何类的set开头的方 ...
- C++11新特性之operator "" xxx(const char *, size_t n)
从C++11开始,我们可以使用以下形式通过常量字符串构造自定义类型, 比如: class Person { public: Person(const std::string& name): _ ...
- Vue实现一个图片懒加载插件(转载)
Vue是可以自定义指令的,最近学习过程中遇见了一个需要图片懒加载的功能,最后参考了别人的代码和思路自己重新写了一遍.以下将详细介绍如何实现自定义指令v-lazyload. 先看如何使用这个指令: &l ...
- Android上执行python脚本-QPython
看书,发现android可以跑python. 尝试了一下. 首先需要在手机上安装python环境,通过安装apk实现,这个apk叫QPython,还有同类的比如SL4A. QPython的官网:htt ...
- 安装pytest
1.安装pytest 2.执行一个用例 进入测试用例目录下,运行以test开头的一个用例. 执行成功. 备注:1.其实测试函数或方法只要以test开头就可以被运行的2.测试文件的名字,其实可以是任意的 ...