自动引用计数(Automatic Reference Counting)

和OC一样,Swift用自动引用计数机制来跟踪和管理你应用程序的内存,大多数情况下,你不需要考虑自己管理内存,Swift会自动帮你管理。当实例对象不再需要时,Swift会自动释放它使用的内存。但是,在有些情况下,ARC需要知道更多你代码之间的关系来帮助你管理内存,本章会描述那些情况并展示如何启用ARC来管理你应用程序的内存。

注意:引用计数只作用于类实例,结构体和枚举是值类型而非引用类型,并且不是以引用形式被存储和传递的。

每当你创建一个新的类实例对象,ARC为它分配一块内存用以存储比如实例类型、实例相关联的存储式属性的值等。当实例不再被需要时,ARC会销毁实例,释放其所占用的内存以供他用。如果实例已经被销毁,那么就不能再使用它或者访问它的属性、方法。为了确保还被需要的实例不被销毁,ARC追踪有多少属性、常量及变量引用到了一个类实例,只要还有至少一个引用指向实例,ARC就不会销毁它。这是通过“强引用”实现的,当你将一个实例赋值给一个常量或者变量、属性时,那个常量或者变量、属性会与实例之间建立一个“强引用”,之所以成为“强”,是因为这个引用会只要这个引用还存在,那么这个实例就不能被释放销毁。

“强引用”在带来便利的时候,会产生一个问题,就是“强引用循环”,比如实例A的某个属性引用了实例B,而实例B的某个属性又引用了A,那么即使其他任何变量都没有引用A和B,他们也各自被一个强引用持有,那么即便他们都不再被需要,它们也不会被销毁,这就造成了内存泄露。

为了解决个这个问题,可以使用“弱引用(weak reference)”和“不持有引用(unowned reference)”。它们可以使你在引用某个实例对象的时候不会持有它,这样实例在互相引用的时候就不会产生强引用循环。

当引用在其生命周期内的某个时刻可能会是nil的时候,使用“弱引用”,当你确定某个引用在其生命周期内都不可能是nil的时候,使用“不持有引用”。因为弱引用是允许没有值的,因此它只能被赋值给可选类型(optional type)。

“弱引用”只能赋值给变量,不能赋值给常量,这是为了表明在运行时其值可能会发生变化的,当弱引用指向的对象已经被销毁时,ARC将弱引用的值改为nil。比如:

class Person {
let name: String
init(name: String) { self.name = name }
var apartment: Apartment?
deinit { println("\(name) is being deinitialized") }
} class Apartment {
let number: Int
init(number: Int) { self.number = number }
weak var tenant: Person? //这里通过weak前缀来声明弱引用变量属性
deinit { println("Apartment #\(number) is being deinitialized") }
}

“不持有引用”和“弱引用”类似,不过它确保永远有值,因此,它总是被赋值给非可选类型,在访问它的时候,也不需要像可选类型那样展开,而是直接访问。此外,当其指向的对象已经被释放时,ARC不能将“不持有引用”设为nil,因为非可选类型的变量不能被设置为nil。事实上,当你访问一个指向已经被销毁对象的不持有引用时,会触发一个运行时错误。

当把一个闭包赋值给一个实例对象的属性,而闭包内部又引用这个实例时(比如闭包体访问实例的属性值self.property或者访问实例的方法self.method),也会发生“强引用循环”,这是因为闭包和类一样,是引用类型的。Swift提供一种优雅的方式来打破这种“强引用循环”,比如:

class HTMLElement {

    let name: String
let text: String? lazy var asHTML: () -> String = {
if let text = self.text { //这里引用了实例对象的属性
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
} init(name: String, text: String? = nil) {
self.name = name
self.text = text
} deinit {
println("\(name) is being deinitialized")
} }

这里HTMLElement类定义了一个lazy属性asHTML,它的默认值是一个闭包,因为它是属性而不是方法,因此你可以用自定义的函数或者闭包来取代默认值,这里属性被标记为lazy,因为它不需要在一开始就被赋值,只有在需要的时候(即需要生成HTML代码),才会执行这个闭包。正因为它是lazy的属性,因此在闭包体内可以访问self,因为执行它的时候初始化已经完成了。

要解决闭包的强引用循环问题,需要用到“捕获列表”,在定义闭包的时候同时定义其捕获列表,这个列表定义了闭包体内捕获一个或多个引用类型时所遵循的规则。

捕获列表是用一堆中括号定义,其内部的项用逗号隔开,每一个项都是一个关键字(weak或unowned)与一个类实例引用(或者一个变量的定义)组成的对,将捕获列表放在其参数列表之前,如下:

lazy var someClosure: (Int, String) -> String = {
[unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
// closure body goes here
}

这样就解除了闭包和实例对象之间的强引用循环。

最新文章

  1. windows 常用命令整合--脚本工具
  2. 解决Onedrive经常无法访问的问题
  3. VIM退出命令
  4. visio 交叉线 不出现拱形怎么办?
  5. iOSQuartz2D-01-核心要点
  6. -----------------------------------项目中整理的非常有用的PHP函数库(二)-----------------------------------------------------
  7. Interview with BOA
  8. 自动发送EMAIL
  9. Windows Phone零距离开发(Composite Thread组合线程)
  10. P1230: [Usaco2008 Nov]lites 开关灯
  11. java中封装
  12. 【转】MySql数据库--mysql_real_escape_string()函数
  13. 《深入了解 Linq to SQL》之对象的标识 —— 麦叔叔呕心呖血之作
  14. Revit二次开发初体验
  15. 基于线程池的多线程售票demo(原创)
  16. 一维信号频谱图仿真——matlab
  17. CSRFGuard工具介绍
  18. IntelliJ IDEA 注册码激活
  19. 超详细设置Idea类注释模板和方法注释模板
  20. python 全栈开发,Day126(创业故事,软件部需求,内容采集,显示内容图文列表,MongoDB数据导入导出JSON)

热门文章

  1. Locker
  2. javascript面向对象实例
  3. Sunday算法(字符串查找、匹配)
  4. 高薪诚聘.NET MVC开发工程师
  5. 该不该将变量设为 null ?
  6. ipv4头部分析,读书笔记3
  7. 程序语言的奥妙:算法解读 &mdash;&mdash;读书笔记
  8. Java正则表达式获取网页所有网址和链接文字
  9. 回调函数与DOM事件
  10. 使用Log.isLoggable方法