本文是學習《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