@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模式下,苹果会自动进行内存管理,不需要我们手动去管理内存.这对于苹果开发者来说,省去了很多事情,不用再每天为了内存管理浪费掉宝贵的开发时间.大家都知道, ...
随机推荐
- 爬虫相关--requests库
requests的理想:HTTP for Humans 一.八个方法 相比较urllib模块,requests模块要简单很多,但是需要单独安装: 在windows系统下只需要在命令行输入命令 pip ...
- Hadoop HA 搭建
Hadoop HA 什么是 HA HA是High Available缩写,是双机集群系统简称,指高可用性集群,是保证业务连续性的有效解决方案,一般有两个或两个以上的节点,且分为活动节点及备用节点.通常 ...
- 只需五分钟-用Maven快速搭建Spring Cloud微服务
Maven安装手册 1.准备安装包 安装包: apache-maven-3.5.4-bin.zip (最好JDK 1.7及以上版本) 集成包: eclipse-maven3-plugin.zip 2 ...
- c# 处理串行化对象的版本变化
- apache 防盗链
方法1:Apache防盗链的第一种实现方法,可以用rewrite实现 (1.)首先要确认Apache的rewrite module可用,打开 httpd.conf 文件,如果前面有注释去掉 LoadM ...
- 运维开发笔记整理-使用Django编写helloworld
运维开发笔记整理-使用Django编写helloworld 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.创建Django项目 1>.创建Django项目 djang ...
- Spring Aop织入点语法
Aspectj织入点语法: 1.execution(public * *(..)) 任何类的任何返回值的任何方法 2.execution(* set*(..)) 任何类的set开头的方 ...
- Windows设置多用户同时远程登录
1.在键盘上按Win+R键(也可以在开始菜单右键然后选择运行),在运行的输入框里面输入"gpedit.msc"命令.然后点击确定 2.在“计算机组策略”中依次展开 计算机配置- ...
- java只能的round,ceil,floor方法的使用
三者均位于java.lange包下的Math类中 round: 在原来数字的基础上加上0.5后向下取整, 例如: Math.floor(11.5)=12; Math.floor(-11.5)=-11( ...
- python-Redis cluster基础指标监控
#!/usr/local/python/shims/python from rediscluster import StrictRedisCluster ''' 需要在宿主机python中安装redi ...