@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模式下,苹果会自动进行内存管理,不需要我们手动去管理内存.这对于苹果开发者来说,省去了很多事情,不用再每天为了内存管理浪费掉宝贵的开发时间.大家都知道, ...
随机推荐
- element-ui默认样式修改
来自 :https://blog.csdn.net/wangguoyu1996/article/details/81394707 侵删 我们在使用element-ui的时候经常会遇到需要修改组件默认样 ...
- 使用js解决response.sendRedirect("...")重定向URL之后出现跨域问题
背景: 本系统与门户系统单点登录时候,需要重定向到门户系统的登录页面,可是如果长时间没有操作的话,session会话失效,就需要跳转到登录页面. 所以在使用 response.sendRedirect ...
- C++ 虚表虚函数怎么就实现了多态?
虚表vftable,编译器为每个拥有虚函数的类都建有一张虚函数表,里面存有虚函数的入口指针(地址).在类对象的内存布局中,先是一个vfptr虚表指针,指向虚表首地址,而后通过偏移量的形式来访问虚表中的 ...
- 九、分组查询详解(group by & having)
本篇内容 分组查询语法 聚合函数 单字段分组 多字段分组 分组前筛选数据 分组后筛选数据 where和having的区别 分组后排序 where & group by & having ...
- Gitlab CI/CD任务一直处于pending
在注册Runner时候这里输入了tag,这里指的是runner的标签,可以设置多个 ,分别用 ,号分割 .gitlab-ci.yml文件中 stages: - pull - package - bu ...
- VISION控制器标定及网络分析工具
VISION 标定和数据采集软件是一个强大的集成工具包,各个工具包可以无缝组合在一起,提供集成的可定制的应用程序,从而能够实现完整的标定和数据分析功能,包括从电子控制单元及外部源收集数据,测量输入和输 ...
- MyBatis3_[tp_38~]_动态sql_if_判断&OGNL_where标签_
笔记要点出错分析与总结 /** 笔记: * 查询的时候,如果某些条件,没带可能SQL拼装会有问题; * 1.-->给where 后面加上 1=1, 以后的条件都and XXX * 2. < ...
- P2882 [USACO07MAR]Face The Right Way [贪心+模拟]
题目描述 N头牛排成一列1<=N<=5000.每头牛或者向前或者向后.为了让所有牛都 面向前方,农夫每次可以将K头连续的牛转向1<=K<=N,求操作的最少 次数M和对应的最小K ...
- master-worker常驻型程序代码修改哪些需要重启master或者worker
之前在yii的项目里用redis作为消息队列,现在很多任务需要延迟需求,于是把之前redis的消息队列替换成了rabbitmq 于是使用yii的yii2-queue这个组件 但是由于提供的yii qu ...
- Postgresql Useful SQL/Commands
Update records ' and a.subscriber_id=b.subscriber_id; Connections select count(*) from pg_stat_activ ...