自动引用计数(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
}

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

Swift学习笔记十五的更多相关文章

  1. Swift 学习笔记十五:扩展

    扩展就是向一个已有的类.结构体或枚举类型加入新功能(functionality).扩展和 Objective-C 中的分类(categories)相似.(只是与Objective-C不同的是,Swif ...

  2. python3.4学习笔记(十五) 字符串操作(string替换、删除、截取、复制、连接、比较、查找、包含、大小写转换、分割等)

    python3.4学习笔记(十五) 字符串操作(string替换.删除.截取.复制.连接.比较.查找.包含.大小写转换.分割等) python print 不换行(在后面加上,end=''),prin ...

  3. (转载)西门子PLC学习笔记十五-(数据块及数据访问方式)

    一.数据块 数据块是在S7 CPU的存储器中定义的,用户可以定义多了数据块,但是CPU对数据块数量及数据总量是有限制的. 数据块与临时数据不同,当逻辑块执行结束或数据块关闭,数据块中的数据是会保留住的 ...

  4. (C/C++学习笔记) 十五. 构造数据类型

    十五. 构造数据类型 ● 构造数据类型概念 Structured data types 构造数据类型 结构体(structure), 联合体/共用体 (union), 枚举类型(enumeration ...

  5. Swift学习笔记十六:协议

    Protocol(协议)用于统一方法和属性的名称,而不实现不论什么功能. 协议可以被类.枚举.结构体实现.满足协议要求的类,枚举,结构体被称为协议的遵循者. 遵循者须要提供协议指定的成员,如属性,方法 ...

  6. swift学习第十五天:闭包

    闭包 闭包的介绍 闭包和OC中的block非常相似 OC中的block是匿名的函数 Swift中的闭包是一个特殊的函数 block和闭包都经常用于回调 注意:闭包和block一样,第一次使用时可能不习 ...

  7. MySQL学习笔记十五:优化(2)

    一.数据库性能评测关键指标 1.IOPS:每秒处理的IO请求次数,这跟磁盘硬件相关,DBA不能左右,但推荐使用SSD. 2.QPS:每秒查询次数,可以使用show status或mysqladmin ...

  8. Swift学习笔记十四

    Deinitialization 当类的实例对象即将要被释放时,会立即调用deinitializer,通过deinit关键字来定义deinitializer,和initializer一样,它也只存在于 ...

  9. Swift学习笔记十二

    方法 方法就是和某种特定类型相关联的函数.类.结构体.枚举都可以定义实例方法和类型方法.类型方法和OC中的类方法类似. 结构体和枚举也可以定义方法是Swift与C/OC之间很大的一个区别,在OC中,只 ...

随机推荐

  1. Java之--Java语言基础组成—数组

    Java语言基础组成-数组 Java语言由8个模块构成,分别为:关键字.标识符(包名.类名.接口名.常量名.变量名等).注释.常量和变量.运算符.语句.函数.数组. 本片主要介绍Java中的数组,数组 ...

  2. 可进行JavaScript代码测试与调试的12个网站

    概述:JavaScript是网站前端开发最为重要的一门编程语言,本文收集了能够在线测试与调试JavaScript代码的12个网站 1.JS Bin JS bin是一个为JavaScript和CSS爱好 ...

  3. 基于OSGI.Net的图形界面系统

    在2013年的十月份有幸接触了osgi.net和iopenworks的创始人,了解和学习的插件式开发,开始了后台数据的处理生涯. 第一个有图形界面的系统——智能农业的环境监测系统,其实在这个系统中所有 ...

  4. 【Unity入门】编辑器常用视图介绍

    版权声明:本文为博主原创文章,转载请注明出处. 打开Unity编辑器的主窗口,在窗口的右上角可以看到有个“Layout”按钮.这是用来对Unity编辑器主窗口上面的各个窗口面板进行布局的.通常情况下我 ...

  5. python3 多线程的基本用法

    #coding=utf-8 import threading #导入threading包 from time import sleep import time   def task1():     p ...

  6. 【转】Linux Page Cache的工作原理

    1 .前言 自从诞生以来,Linux 就被不断完善和普及,目前它已经成为主流通用操作系统之一,使用得非常广泛,它与Windows.UNIX 一起占据了操作系统领域几乎所有的市场份额.特别是在高性能计算 ...

  7. 关系数据库&amp;&amp;NoSQL数据库

    在过去,我们只需要学习和使用一种数据库技术,就能做几乎所有的数据库应用开发.因为成熟稳定的关系数据库产品并不是很多,而供你选择的免费版本就更加少了,所以互联网领域基本上都选择了免费的MySQL数据库. ...

  8. RAD XE10 Seattle

    RAD Studio 10 Seattle RAD XE10 Seattle RAD 10 Seattle c++builder 10 Seattle Delphi 10 Seattle http:/ ...

  9. hdu 1281 棋盘游戏

    http://acm.hdu.edu.cn/showproblem.php?pid=1281 棋盘游戏 Time Limit: 2000/1000 MS (Java/Others)    Memory ...

  10. UVaLive 6801 Sequence (计数DP)

    题意:给定一个序列,有 n 个数,只有01,然后你进行k次操作,把所有的1变成0,求有多种方法. 析:DP是很明显的,dp[i][j] 表示进行第 i 次操作,剩下 j 个1,然后操作就两种,把1变成 ...