16.ARC
Swift 使用自动引用计数(ARC)机制来跟踪和管理应用程序的内存。通常情况下,Swift 内存管理机制会一直起作用,我们无须自己来考虑内存的管理。ARC 会在类的实例不再被使用时,自动释放其占用的内存。
引用计数仅仅应用于类的实例。结构体和枚举类型是值类型,不是引用类型,也不是通过引用的方式存储和传递。
1.自动引用计数的工作机制
- 当创建一个类的新的实例的时候,ARC 会分配一块内存来储存该实例信息。内存中会包含实例的类型信息,以及这个实例所有相关的存储型属性的值。
- 当实例不再被使用时,ARC 释放实例所占用的内存,并让释放的内存能挪作他用。这确保了不再被使用的实例,不会一直占用内存空间。
- 为了确保使用中的实例不会被销毁,ARC 会跟踪和计算每一个实例正在被多少属性,常量和变量所引用。哪怕实例的引用数为1,ARC都不会销毁这个实例。
- 将实例赋值给属性、常量或变量,它们都会创建此实例的强引用。之所以称之为“强”引用,是因为它会将实例牢牢地保持住,只要强引用还在,实例是不允许被销毁的。
2.自动引用计数实践
class Person
{
let name: String; init(name: String)
{
self.name = name;
print("\(name) is being initialized");
} deinit
{
print("\(name) is being deinitialized");
}
} //这里还没有创建
var reference1: Person?;
var reference2: Person?; //创建实例
reference1 = Person(name: "GofLee"); //这里才会创建并打印:"GofLee is being initialized\n"
reference2 = reference1; reference1 = nil;
reference2 = nil; //这个时候才会执行析构过程并打印:"GofLee is being deinitialized\n"
3.类实例之间的循环强引用
- 如果两个类实例互相持有对方的强引用,因而每个实例都让对方一直存在,这就是所谓的循环强引用。
下面的例子演示了循环强引用是怎么产生的:
class Person
{
let name: String;
var apartment: Apartment?; init(name: String)
{
self.name = name;
} deinit
{
print("\(name) is being deinitialized");
}
} class Apartment
{
let unit: String;
var tenant: Person?; init(unit: String)
{
self.unit = unit;
} deinit
{
print("Apartment \(unit) is being deinitialized");
}
} var john: Person? = Person(name: "John Appleseed");
var unit4A: Apartment? = Apartment(unit: "4A"); john!.apartment = unit4A;
unit4A!.tenant = john; john = nil;
unit4A = nil;
从上面的代码可以看到,Person实例拥有一个指向Apartment实例的强引用,而Apartment实例也拥有一个指向Person实例的强引用。如下图所示:

当断开john和unit4A变量所持有的强引用时,引用计数并不会降为0,实例也不会被 ARC 销毁。图解如下:

4.解决实例之间的循环强引用
Swift 提供了两种办法用来解决在使用类的属性时所遇到的循环强引用问题:弱引用(weak reference)和无主引用(unowned reference)。
- 弱引用和无主引用允许循环引用中的一个实例引用另外一个实例而不保持强引用。这样实例能够互相引用而不产生循环强引用。
- 对于生命周期中会变为nil的实例使用弱引用。相反地,对于初始化赋值后再也不会被赋值为nil的实例,使用无主引用。
- 和弱引用类似,无主引用不会牢牢保持住引用的实例。和弱引用不同的是,无主引用是永远有值的。因此,无主引用总是被定义为非可选类型。
示例1:弱引用解决上面循环强引用的问题
class Person
{
let name: String;
var apartment: Apartment?;
//weak var apartment: Apartment?; init(name: String)
{
self.name = name;
} deinit
{
print("\(name) is being deinitialized");
}
} class Apartment
{
let unit: String;
weak var tenant: Person?; //注意此处的weak init(unit: String)
{
self.unit = unit;
} deinit
{
print("Apartment \(unit) is being deinitialized");
}
} var john: Person? = Person(name: "John Appleseed");
var unit4A: Apartment? = Apartment(unit: "4A"); john!.apartment = unit4A;
unit4A!.tenant = john; john = nil; //打印"John Appleseed is being deinitialized\n"
unit4A = nil; //打印"Apartment 4A is being deinitialized\n"
从上面的代码可以看到,这里Apartment的tenant属性被声明为弱引用,如下图所示:

当调用john = nil时,由于没有指向Person实例的强引用,所以实例会被销毁,图解如下:

此时唯一剩下的指向Apartment实例的强引用来自于变量unit4A。如果断开这个强引用,再也没有指向Apartment实例的强引用了:

示例2:无主引用
//示例业务模型说明:一个客户可能有或者没有信用卡,但是一张信用卡总是关联着一个客户。为了表示这种关系,Customer类有一个可选类型的card属性,但是CreditCard类有一个非可选类型的customer属性。
class Customer
{
let name: String;
var card: CreditCard?; init(name: String)
{
self.name = name;
} deinit
{
print("\(name) is being deinitialized");
}
} class CreditCard
{
let number: UInt64;
unowned let customer: Customer; init(number: UInt64, customer: Customer)
{
self.number = number;
self.customer = customer;
} deinit
{
print("Card #\(number) is being deinitialized");
}
} var john: Customer?;
john = Customer(name: "John"); john!.card = CreditCard(number: , customer: john!);
john = nil;
【小结】:
- 两个属性的值都允许为nil,并会潜在的产生循环强引用。这种场景最适合用弱引用来解决(如上面的Person和Apartment)。
- 一个属性的值允许为nil,而另一个属性的值不允许为nil,这也可能会产生循环强引用。这种场景最适合通过无主引用来解决(如上面的
Customer和CreditCard)。 - 两个属性都必须有值,并且初始化完成后永远不会为nil。在这种场景中,需要一个类使用无主属性,而另外一个类使用隐式解析可选属性。示例如下:
//示例业务模型说明:每个国家必须有城市,每个城市必须属于一个国家。为了实现这种关系,Country类拥有一个capitalCity属性,而City类有一个country属性
class Country
{
let name: String;
var capitalCity: City!; //隐式解析可选类型,默认值为nil 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: "China", capitalName: "Beijing");
print("\(country.name)'s capital city is called \(country.capitalCity.name)");
5.闭包引起的循环强引用
先看一个示例:
class HTMLElement
{
let name: String; //HTML元素名称
let text: String?; //HTML元素显示的文本 lazy var asHTML: Void -> 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
{
print("\(name) is being deinitialized");
}
} var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world");
print(paragraph!.asHTML());
paragraph = nil;
当调用paragraph = nil时,我们可以看到HTMLElement的deinit方法并没有调用。说明paragraph没有释放,现在我们用图来分析一下paragraph和默认闭包之间的引用关系:

6.解决闭包引起的循环强引用
在定义闭包时同时定义捕获列表作为闭包的一部分,声明每个捕获的引用为弱引用或无主引用,而不是强引用。应当根据代码关系来决定使用弱引用还是无主引用。
- 在闭包和捕获的实例总是互相引用并且总是同时销毁时,将闭包内的捕获定义为
无主引用。 - 相反的,在被捕获的引用可能会变为
nil时,将闭包内的捕获定义为弱引用。弱引用总是可选类型,并且当引用的实例被销毁后,弱引用的值会自动置为nil。这使我们可以在闭包体内检查它们是否存在。
示例:解决上面闭包的循环强引用
class HTMLElement
{
let name: String;
let text: String?; lazy var asHTML: Void -> String = {
[unowned self] (void) -> String 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
{
print("\(name) is being deinitialized");
} }
16.ARC的更多相关文章
- 我的Objective-C系列文章
做iOS开发有一段时间了,也有自己上线的App产品,也在坚持着发表技术博客总结自己所学的东西.在写博客的时候虽然博文中不免有错别字,但每句话都是在斟酌之后所写的,每篇博客所粘贴的代码都是经过调试运行无 ...
- ios 面试题 0
1.__block和__weak修饰符的区别: 1.__block不管是ARC还是MRC模式下都可以使用,可以修饰对象,还可以修饰基本数据类型. 2.__weak只能在ARC模式下使用,也只能修饰 ...
- Objective-C系列
我的Objective-C系列文章和坚持写博客的感想 做iOS开发有一段时间了,也有自己上线的App产品,也在坚持着发表技术博客总结自己所学的东西.在写博客的时候虽然博文中不免有错别字,但每句话都 ...
- Swift学习目录
本学习基于苹果官方Swift学习材料,保留了原版90%左右的内容(一些项目开发中基本不用的知识点没有整理),并根据理解进行整理.如对原版感兴趣,可以直接单击链接阅读和学习. 第一部分 基础篇 1.基本 ...
- LaTeX:图形的填充(生成阴影图形)
将内网和外网看到的综合整理. 韦恩图Venn \documentclass{standalone} \usepackage{tikz} %导出为图片需要安装imagemagick %https://t ...
- Canvas链式操作
Canvas 链式操作 canvas有个非常麻烦的地方就是不支持链式操作,导致书写极其繁琐,刚刚学习了canvas的链式操作. 下面是代码 改进之后的写法,犀利得多啊! 1.canvas = ...
- ARC内存管理机制详解
ARC在OC里面个人感觉又是一个高大上的牛词,在前面Objective-C中的内存管理部分提到了ARC内存管理机制,ARC是Automatic Reference Counting---自动引用计数. ...
- iOS开发ARC内存管理技术要点
本文来源于我个人的ARC学习笔记,旨在通过简明扼要的方式总结出iOS开发中ARC(Automatic Reference Counting,自动引用计数)内存管理技术的要点,所以不会涉及全部细节.这篇 ...
- block使用小结、在arc中使用block、如何防止循环引用
引言 使用block已经有一段时间了,感觉自己了解的还行,但是几天前看到CocoaChina上一个关于block的小测试主题: [小测试]你真的知道blocks在Objective-C中是怎么工作的吗 ...
随机推荐
- uimodalpresentationformsheet resize ios7
CROHomeCRAAddController *temp =[[CROHomeCRAAddControlleralloc] init]; temp.modalTransitionStyle = UI ...
- HBase 系统架构
HBase是Apache Hadoop的数据库,能够对大型数据提供随机.实时的读写访问.HBase的目标是存储并处理大型的数据.HBase是一个开源的,分布式的,多版本的,面向列的存储模型.它存储的是 ...
- Android源码分析--CircleImageView 源码详解
源码地址为 https://github.com/hdodenhof/CircleImageView 实际上就是一个圆形的imageview 的自定义控件.代码写的很优雅,实现效果也很好, 特此分析. ...
- Selenium2Library系列 keywords 之 _SelectElementKeywords 之 select_all_from_list(self, locator)
def select_all_from_list(self, locator): """Selects all values from multi-select list ...
- CString类Format()的用法 .xml
pre{ line-height:1; color:#9f1d66; background-color:#f0f0f0; font-size:16px;}.sysFunc{color:#5d57ff; ...
- 原创 | 《地狱边境》登顶50国iOS下载榜,恐怖游戏或是独立开发者突破口(转)
文/手游那点事 Jagger 与大厂强IP称霸的App Store畅销榜相比,付费榜一向是独立游戏的温床.高质量的独立游戏并不需要在推广营销中投入太多成本,依靠过硬的品质和口碑在付费榜中缓缓上升造就高 ...
- nagios监控远程主机服务可能出现的问题
1.使用插件NRPE监控命令不存在 在添加服务的时候,命令配置文件中需要传递一个参数,那么在监控服务配置文件中,需要添加一个!表示后面的为参数. 出现未定义的命令,查看被监控主机上的配置文件,添加监控 ...
- Intent传递数据
方式比较多,先看看代码,一会儿再总结. activity_main.xml <RelativeLayout xmlns:android="http://schemas.android. ...
- atoi()函数的实现
atoi()函数的功能:将字符串转换成整型数:atoi()会扫描参数nptr字符串,跳过前面的空格字符,直到遇上数字或正负号才开始做转换,而再遇到非数字或字符串时('\0')才结束转化,并将结果返回( ...
- 【boost】BOOST_LOCAL_FUNCTION体验
c++11里支持使用lambda在函数内定义本地嵌套函数,将一些算法的判断式定义为本地函数可以使代码更加清晰,同时声明和调用靠近也使得更容易维护.遗憾的是公司开发平台任然停留在vs2008,使用boo ...