@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模式下,苹果会自动进行内存管理,不需要我们手动去管理内存.这对于苹果开发者来说,省去了很多事情,不用再每天为了内存管理浪费掉宝贵的开发时间.大家都知道, ...
随机推荐
- 关于下载gitbash客户端
gitbash的安装包找不到了,最近又在准备新机器的环境,需要安装git客户端. https://www.git-scm.com/,网址找到了,但是下载非常缓慢.搜了一下,都是改host,但是改了也没 ...
- window事件
window事件是较为重要的事件,接下来就讲解一下. 1.获取页面滚动栏的距离 什么是滚动栏,就是网页内容过多时,通过滚轮控制上下显示或者左右显示: 为窗口添加滚动条事件: window.onscro ...
- S5PV210 时钟
CLOCK DOMAINS 时钟域 S5PV210 consists of three clock domains, namely, main system (MSYS), display syste ...
- springboot系列(十)springboot整合shiro实现登录认证
关于shiro的概念和知识本篇不做详细介绍,但是shiro的概念还是需要做做功课的要不无法理解它的运作原理就无法理解使用shiro: 本篇主要讲解如何使用shiro实现登录认证,下篇讲解使用shiro ...
- Win10开启蓝屏信息记录及文件查看位置的方法
蓝屏,是电脑最常见的故障,一般出现蓝屏时都会显示详细的蓝屏错误信息,方便用户排查故障.但是如果系统未开启蓝屏记录,下文介绍蓝屏日志开启及蓝屏日志文件存放位置.我用的是win10系统 蓝屏日志开启方法步 ...
- JAVA设计模式之单例模式(单件模式)—Singleton Pattern
1.什么是单例模式? <Head First 设计模式>中给出如下定义:确保一个类只有一个实例,并提供一个全局访问点. 关键词:唯一实例对象. 2.单例模式的实现方式: 2.1 懒汉式 对 ...
- webpack中如何编写一个plugin
loader和plugin有什么区别呢?什么是loader,什么是plugin. 当我们在源代码里面去引入一个新的js文件或者一个其他格式的文件的时候,这个时候,我们可以借助loader去帮我们处理引 ...
- jQuery 中的 Ajax 方法(节选)
$.ajax() 基本用法: $.ajax({ url: url, // 地址 data: data, // 参数 type: 'POST', // 提交方式 可以选择 post/get 推荐 pos ...
- Python+request 使用pymysql连接数据库mysql的操作《十》
使用指南.pymysql支持python2.7同时也支持python3.x.当前我用的是python2.7.所以过断选择了pymysql的使用,这里注意几点.一般我们连接数据库为了安全起见,都会要求按 ...
- ElementUI 之 Cascader 级联选择器指定 value label
ElementUI 的 Cascader 级联选择器个人觉得很好用,但是对 :options="options" 里的数据格式是有特定要求的:input 框显示的值是 option ...