本文是学习《The Swift Programming Language》整理的相关随笔,基本的语法不作介绍,主要介绍Swift中的一些特性或者与OC差异点。
系列文章:
- Swift4 基础部分:The Basics
- Swift4 基础部分:Basic Operators
- Swift4 基础部分:Strings and Characters
- Swift4 基础部分:Collection Types
- Swift4 基础部分:Control Flow
- Swift4 基础部分:Functions
- Swift4 基础部分:Closures
- Swift4 基础部分: Enumerations
- Swift4 基础部分: Classes and Structures
- Swift4 基础部分: Properties
- Swift4 基础部分: Methods
- Swift4 基础部分: Subscripts
- Swift4 基础部分: Inheritance
- Swift4 基础部分: Initialization
- Swift4 基础部分: Deinitialization
Swift uses Automatic Reference Counting (ARC) to track and
manage your app’s memory usage. In most cases, this means
that memory management “just works” in Swift, and you do
not need to think about memory management yourself. ARC
automatically frees up the memory used by class instances
when those instances are no longer needed.
- Swift中是引用自动引用计数来处理app中的内存占用。也就是说你不需要去考虑app中的内存的管理。当类的实例不再被使用时,ARC会自动释放其占用的内存。
Reference counting only applies to instances of classes.
Structures and enumerations are value types, not reference
types, and are not stored and passed by reference.
- 引用计数仅仅应用于类的实例。结构体和枚举类型是值类型,不是引用类型,也不是通过引用的方式存储和传递。
ARC的工作机制(How ARC Works)
Every time you create a new instance of a class, ARC
allocates a chunk of memory to store information about
that instance. This memory holds information about the
type of the instance, together with the values of any
stored properties associated with that instance.
Additionally, when an instance is no longer needed, ARC
frees up the memory used by that instance so that the
memory can be used for other purposes instead. This
ensures that class instances do not take up space in
memory when they are no longer needed.
- 当你每次创建一个类的新的实例的时候,ARC 会分配一大块内存用来储存实例的信息。内存中会包含实例的类型信息,以及这个实例所有相关属性的值。此外,当实例不再被使用时,ARC会释放实例所占用的内存,并让释放的内存其他使用。这就确保了不再被使用的实例,不会一直占用内存空间。
ARC的实践(ARC in Action)
例子:
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? = Person(name:"xz");
var reference2: Person? = reference1;
var reference3: Person? = reference1;
reference1 = nil;
reference2 = nil;
reference3 = nil;
执行结果:
xz is being initialized
xz is being deinitialized
类实例之间的循环强引用(Strong Reference Cycles Between Class Instances)
循环引用在OC中也是常见的,直接看一个例子:
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; }
var tenant: Person?;
deinit { print("Apartment \(unit) is being deinitialized"); }
}
var john: Person?;
var unit4A: Apartment?;
john = Person(name: "John Appleseed");
unit4A = Apartment(unit: "4A");
// 以下是核心引发的例子
john!.apartment = unit4A;
unit4A!.tenant = john;
john = nil;
unit4A = nil;
解决类实例之间的强循环引用(Resolving Strong Reference Cycles Between Class Instances)
Swift provides two ways to resolve strong reference cycles
when you work with properties of class type: weak
references and unowned references.
- Swift 提供了两种办法用来解决你在使用类的属性时所遇到的循环强引用问题:弱引用(weak reference)和无主引用(unowned reference)。
弱引用(Weak References)
A weak reference is a reference that does not keep a
strong hold on the instance it refers to, and so does not
stop ARC from disposing of the referenced instance. This
behavior prevents the reference from becoming part of a
strong reference cycle. You indicate a weak reference by
placing the weak keyword before a property or variable
declaration.
- 弱引用不会牢牢保持住引用的实例,并且不会阻止 ARC 销毁被引用的实例。这种行为阻止了引用变为循环强引用。声明属性或者变量时,在前面加上weak关键字表明这是一个弱引用。
直接改写一下上述的例子:
class Apartment {
let unit: String;
init(unit: String) { self.unit = unit; }
weak var tenant: Person?; // 注意此处
deinit { print("Apartment \(unit) is being deinitialized"); }
}
执行结果:
John Appleseed is being deinitialized
Apartment 4A is being deinitialized
无主引用(Unowned References)
Like a weak reference, an unowned reference does not keep
a strong hold on the instance it refers to. Unlike a weak
reference, however, an unowned reference is used when the
other instance has the same lifetime or a longer lifetime.
You indicate an unowned reference by placing the unowned
keyword before a property or variable declaration.
- 和弱引用类似,无主引用不会牢牢保持住引用的实例。和弱引用不同的是,无主引用拥有同样或者更长的生命周期相对其他的实例。
An unowned reference is expected to always have a value.
As a result, ARC never sets an unowned reference’s value
to nil, which means that unowned references are defined
using nonoptional types.
- 无主引用一直都是有值的,ARC无法在实例被销毁后将无主引用设为nil,也就是说无主引用是非可选型的。
例子:
class Customer {
let name: String;
var card: CreditCard?;
init(name: String) {
self.name = name;
}
deinit { print("\(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 { print("Card #\(number) is being deinitialized"); }
}
var john: Customer?;
john = Customer(name: "John Appleseed");
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!);
john = nil;
执行结果:
John Appleseed is being deinitialized
Card #1234567890123456 is being deinitialized
无主引用以及隐式解析可选属性(Unowned References and Implicitly Unwrapped Optional Properties)
当相互引用的属性都不允许为nil时,此时就需要使用无主引用+隐式解析可选属性。
例子:
class Country {
let name: String;
var 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");
print("\(country.name)'s capital city is called \(country.capitalCity.name)");
执行结果:
Canada's capital city is called Ottawa
闭包中的循环强引用(Strong Reference Cycles for Closures)
与OC中的block引起的循环强引用一致,直接看一下例子:
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 {
print("\(name) is being deinitialized")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world");
print(paragraph!.asHTML());
paragraph = nil;
执行结果:
<p>hello, world</p>
疑问:
- 为什么
deinit
函数没有执行? 因为asHTML
的闭包"捕获"了self,同时asHTML
的属性持有了闭包的强引用。二者之间产生了循环强引用。
解决闭包引起的循环强引用(Resolving Strong Reference Cycles for Closures)
You resolve a strong reference cycle between a closure and a
class instance by defining a capture list as part of the
closure’s definition.
- 为解决闭包和类实例之间的循环强引用,可以定义闭包时同时定义捕获列表作为闭包的一部分。
具体捕获列表的语法参考如下:
lazy var someClosure: (Int, String) -> String = {
[unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
// closure body goes here
}
Define a capture in a closure as an unowned reference when the
closure and the instance it captures will always refer to each
other, and will always be deallocated at the same time.
Conversely, define a capture as a weak reference when the
captured reference may become nil at some point in the future.
- 将闭包内的捕获定义为无主引用,当闭包和捕获的实例总是互相引用时并且总是同时销毁时。相反的,将闭包内的捕获定义为弱引用,当捕获引用有时可能会是nil时。
上述的例子中,显然无主引用
可以解决上述闭包引起的循环强引用问题,具体代码如下:
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
[unowned self] () ->String in // 可简写为[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 {
print("\(name) is being deinitialized")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world");
print(paragraph!.asHTML());
paragraph = nil;
执行结果:
<p>hello, world</p>
p is being deinitialized