【Swift学习】Swift编程之旅---ARC(二十)
Swift使用自动引用计数(ARC)来跟踪并管理应用使用的内存。大部分情况下,这意味着在Swift语言中,内存管理"仍然工作",不需要自己去考虑内存管理的事情。当实例不再被使用时,ARC会自动释放这些类的实例所占用的内存。然而,在少数情况下,为了自动的管理内存空间,ARC需要了解关于你的代码片段之间关系的更多信息。本章描述了这些情况,并向大家展示如何打开ARC来管理应用的所有内存空间。
class Person {
let name: String
init(name: String) {
self.name = name
println("\(name) is being initialized")
}
deinit {
println("\(name) is being deinitialized")
}
}
var reference1: Person?
var reference2: Person?
var reference3: Person?
现在我们创建一个新的Person实例,并且将它赋值给上述三个变量中的一个:
reference1 = Person(name: "John Appleseed")
// prints "Jonh Appleseed is being initialized"
reference2 = reference1
reference3 = reference2
reference1 = nil
reference2 = nil
直到第三个也是最后一个强引用被破坏,ARC才会销毁Person的实例,这时,有一点非常明确,你无法继续使用Person实例:
referenece3 = nil
// 打印 “John Appleseed is being deinitialized”
class Person {
let name: String
init(name: String) { self.name = name }
var apartment: Apartment?
deinit { println("\(name) is being deinitialized") }
}
class Apartment {
let unit: Int
init(unit: Int) { self.unit= unit }
var tenant: Person?
deinit { println("Apartment #\(number) is being deinitialized") }
}
var john: Person?
var unit4A: Apartment?
现在,你可以创建特定的Person实例以及Apartment实例,并赋值给john和number73:
jhon = Person(name: "John Appleseed")
unit4A = Apartments(number: 4A)
下面的图表明了在创建以及赋值这两个实例后强引用的关系。john拥有一个Person实例的强引用,unit4A拥有一个Apartment实例的强引用:

现在你可以将两个实例关联起来,一个人拥有一所公寓,一个公寓也拥有一个房客。注意:用感叹号(!)来展开并访问可选类型的变量,只有这样这些变量才能被赋值:
john!.apartment = unit4A
unit4A!.tenant = john
实例关联起来后,强引用关系如下图所示

关联这俩实例生成了一个强循环引用,Person实例和Apartment实例各持有一个对方的强引用。因此,即使你破坏john和number73所持有的强引用,引用计数也不会变为0,因此ARC不会销毁这两个实例
john = nil
unit4A = nil
当上面两个变量赋值为nil时,没有调用任何一个析构方法。强引用阻止了Person和Apartment实例的销毁,进一步导致内存泄漏。
避免强引用循环
Swift提供两种方法避免强引用循环:弱引用和非持有引用。
对于生命周期中引用会变为nil的实例,使用弱引用;对于初始化时赋值之后引用再也不会赋值为nil的实例,使用非持有引用。
弱引用
弱引用不会增加实例的引用计数,因此不会阻止ARC销毁被引用的实例,声明属性或者变量的时候,关键字weak表明引用为弱引用。弱引用只能声明为变量类型,因为运行时它的值可能改变。弱引用绝对不能声明为常量
因为弱引用可以没有值,所以声明弱引用的时候必须是可选类型的。在Swift语言中,推荐用可选类型来作为可能没有值的引用的类型。
下面的例子和之前的Person和Apartment例子相似,除了一个重要的区别。这一次,我们声明Apartment的tenant属性为弱引用:
class Person {
let name: String
init(name: String) { self.name = name }
var apartment: Apartment?
deinit { print("\(name) is being deinitialized") }
}
class Apartment {
let unit: String
init(unit: String) { self.unit = unit }
weak var tenant: Person?
deinit { print("Apartment \(unit) is being deinitialized") }
}
然后创建两个变量(john和unit4A)的强引用,并关联这两个实例:
var john: Person?
var unit4A: Apartment? john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A") john!.apartment = unit4A
unit4A!.tenant = john
下面是引用的关系图:

Person的实例仍然是Apartment实例的强引用,但是Apartment实例则是Person实例的弱引用。这意味着当破坏john变量所持有的强引用后,不再存在任何Person实例的强引用:

既然不存在Person实例的强引用,那么该实例就会被销毁:
class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit { println("\(name) is being deinitialized")
}
class CreditCard {
let number: Int
unowned let customer: Customer
init(number: Int, customer: Customer) {
self.number = number
self.customer = customer
}
deinit { println("Card #\(number) is being deinitialized")
}
var john: Customer?
现在创建一个Customer实例,然后用它来初始化CreditCard实例,并把刚创建出来的CreditCard实例赋值给Customer的card属性:
john = Customer(name: "John Appleseed")
john!.card = CreditCard(number: 1234_5678_9012_3456, customer:john!)
此时的引用关系如下图所示

因为john对CreditCard实例是非持有引用,当破坏john变量持有的强引用时,就没有Customer实例的强引用了

此时Customer实例被销毁。然后,CreditCard实例的强引用也不复存在,因此CreditCard实例也被销毁
john = nil
// 打印"John Appleseed is being deinitialized"
// 打印"Card #1234567890123456 is being deinitialized"
非持有引用以及隐式展开的可选属性
下面的例子定义了两个类,Country和City,都有一个属性用来保存另外的类的实例。在这个模型里,每个国家都有首都,每个城市都隶属于一个国家。所以,类Country有一个capitalCity属性,类City有一个country属性:
class Country {
let name: String
let capitalCity: City!
init(name: String, capitalName: String) {
self.name = name
self.capitalCity = City(name: capitalName, country: self)
}
}
class City {
let name: String
unowned let country: Country
init(name: String, country: Country) {
self.name = name
self.country = country
}
}
var country = Country(name: "Canada", capitalName: "Ottawa")
println("\(country.name)'s captial city is called \(country.capitalCity.name)")
// 打印"Canada's capital city is called Ottawa"
在上面的例子中,使用隐式展开的可选值满足了两个类的初始化函数的要求。初始化完成后,capitalCity属性就可以做为非可选值类型使用,却不会产生强引用环。
闭包的强引用循环
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")
}
}
闭包使用了self(引用了self.name和self.text),因此闭包占有了self,这意味着闭包又反过来持有了HTMLElement实例的强引用。这样就产生了强引用环
避免闭包产生的强引用循环
定义捕获列表
lazy var someClosure: (Int, String) -> String = {
[unowned self] (index: Int, stringToProcess: String) -> String in
// closure body goes here
}
如果闭包没有指定参数列表或者返回类型(可以通过上下文推断),那么占有列表放在闭包开始的地方,跟着是关键字in:
lazy var someClosure: () -> String = {
[unowned self] in
// closure body goes here
}
前面提到的HTMLElement例子中,非持有引用是正确的解决强引用的方法。这样编码HTMLElement类来避免强引用环:
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
[unowned self] in
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")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
println(paragraph!.asTHML())
// 打印"<p>hello, world</p>"
引用关系如下图

这一次,闭包以无主引用的形式占有self,并不会持有HTMLElement实例的强引用。如果赋值paragraph为nil,HTMLElement实例将会被销毁,并能看到它的deinitializer打印的消息。
paragraph = nil
// 打印"p is being deinitialized"
【Swift学习】Swift编程之旅---ARC(二十)的更多相关文章
- JAVA之旅(二十九)——文件递归,File结束练习,Properties,Properties存取配置文件,load,Properties的小练习
JAVA之旅(二十九)--文件递归,File结束练习,Properties,Properties存取配置文件,load,Properties的小练习 我们继续学习File 一.文件递归 我们可以来实现 ...
- JAVA之旅(二十六)——装饰设计模式,继承和装饰的区别,LineNumberReader,自定义LineNumberReader,字节流读取操作,I/O复制图片
JAVA之旅(二十六)--装饰设计模式,继承和装饰的区别,LineNumberReader,自定义LineNumberReader,字节流读取操作,I/O复制图片 一.装饰设计模式 其实我们自定义re ...
- JAVA之旅(二十二)——Map概述,子类对象特点,共性方法,keySet,entrySet,Map小练习
JAVA之旅(二十二)--Map概述,子类对象特点,共性方法,keySet,entrySet,Map小练习 继续坚持下去吧,各位骚年们! 事实上,我们的数据结构,只剩下这个Map的知识点了,平时开发中 ...
- JAVA之旅(二十八)——File概述,创建,删除,判断文件存在,创建文件夹,判断是否为文件/文件夹,获取信息,文件列表,文件过滤
JAVA之旅(二十八)--File概述,创建,删除,判断文件存在,创建文件夹,判断是否为文件/文件夹,获取信息,文件列表,文件过滤 我们可以继续了,今天说下File 一.File概述 文件的操作是非常 ...
- JAVA之旅(二十五)——文件复制,字符流的缓冲区,BufferedWriter,BufferedReader,通过缓冲区复制文件,readLine工作原理,自定义readLine
JAVA之旅(二十五)--文件复制,字符流的缓冲区,BufferedWriter,BufferedReader,通过缓冲区复制文件,readLine工作原理,自定义readLine 我们继续IO上个篇 ...
- JAVA之旅(二十四)——I/O流,字符流,FileWriter,IOException,文件续写,FileReader,小练习
JAVA之旅(二十四)--I/O流,字符流,FileWriter,IOException,文件续写,FileReader,小练习 JAVA之旅林林总总也是写了二十多篇了,我们今天终于是接触到了I/O了 ...
- JAVA之旅(二十)—HashSet,自定义存储对象,TreeSet,二叉树,实现Comparator方式排序,TreeSet小练习
JAVA之旅(二十)-HashSet,自定义存储对象,TreeSet,二叉树,实现Comparator方式排序,TreeSet小练习 我们继续说一下集合框架 Set:元素是无序(存入和取出的顺序不一定 ...
- Swift学习——Swift基础具体解释(一)
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/zhenyu5211314/article/details/34807025 注:由于基础部分在Swi ...
- 【Swift学习】Swift编程之旅---枚举(十二)
枚举为一组相关的值定义一个共同的类型,并允许您在代码中的以类型安全的方式中使用这些值,在 Swift 中,枚举类型是一等(first-class)类型.它们采用了很多传统上只被类所支持的特征,例如计算 ...
随机推荐
- Linux计划任务crontab运行脚本不正确的问题
问题的由来 写好的程序希望在崩溃之后能够自启动,于是利用linux的crontab功能,添加一个计划任务,每分钟执行一个脚本查看需要监控的进程是否还在,如果不在则启动之,否则不做任何事情.这么一个简单 ...
- [Asp.net 开发系列之SignalR篇]专题六:使用SignalR实现消息提醒
一.引言 前面一篇文章我介绍了如何使用SignalR实现图片的传输,然后对于即时通讯应用来说,消息提醒是必不可少的.现在很多网站的都有新消息的提醒功能.自然对于SignalR系列也少不了这个功能的实现 ...
- activeMq笔记
安装 下载地址:http://activemq.apache.org/download.html 安装教程: http://gerrard-ok.iteye.com/blog/1766203 解压缩: ...
- Android 开发必备知识:我和 Gradle 有个约会
腾讯Bugly特约作者:霍丙乾 0.讲个故事 0.1 Ant,我还真以为你是只蚂蚁 真正开始近距离接触编程其实是在2012年,年底的时候带我的大哥说,咱们这个 app 发布的时候手动构建耗时太久,研究 ...
- MSBuild 中的 PropertyGroup、ItemGroup 和 ItemMetadata
在软件项目不断的进展中,MSBuild 脚本可能几个月都不会被修改,因为通常编译和发布的目录是不经常变化的. 但,一旦某天你需要修改了,看到那一堆 $(Something). @(Something) ...
- MySQL--将MySQL数据导入到SQL Server
随着时代的进步,社会的发展,各种技术层出不穷五花八门乱七八糟数不胜数(写作文呢!!!) 不扯废话,简单而言,很多公司都会同时使用多种数据库,因此数据在不同数据库之间导入导出就成为一个让人蛋疼的问题,对 ...
- SlipHover,能感知鼠标方向的图片遮罩效果jQuery插件
接上一篇博文,介绍完jQuery插件开发后这里上一个自己的作品,也是初次实践,小有成就的感觉. 话说这个插件年前就写好了,然后挂到GitHub,然后就偷偷看着Google Analysis心中暗自激动 ...
- (翻译)正确实施DevOps-The Lay of the Land
原文地址:http://www.drdobbs.com/architecture-and-design/getting-devops-right-the-lay-of-the-land/2400626 ...
- WPF路线图白皮书: 2015及未来
介绍 当2006年微软首次推出Windows Presentation Foundation(WPF)时,它代表了应用程序的设计和开发又向前跨出了巨大的一步.它简化了GUI应用程序的开发,有助于UI和 ...
- 大叔也说Xamarin~Android篇~支付宝SDK的错误与解决
回到目录 在为android程序引入支付宝后,可能没有那么顺利,有能出现各种各样的问题,大叔在引用支付宝后就出现了APL64的错误,在网上找不很多资料都不对正,还是在官网找到了几句话,说到了点子上,具 ...