Swift4 基礎部分: Automatic Reference Counting(自動引用計數(shù))

本文是學習《The Swift Programming Language》整理的相關隨筆,基本的語法不作介紹,主要介紹Swift中的一些特性或者與OC差異點宏怔。

系列文章:

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中是引用自動引用計數(shù)來處理app中的內(nèi)存占用愉豺。也就是說你不需要去考慮app中的內(nèi)存的管理。當類的實例不再被使用時隙袁,ARC會自動釋放其占用的內(nèi)存。
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.
  • 引用計數(shù)僅僅應用于類的實例弃榨。結(jié)構(gòu)體和枚舉類型是值類型菩收,不是引用類型,也不是通過引用的方式存儲和傳遞鲸睛。

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.
  • 當你每次創(chuàng)建一個類的新的實例的時候娜饵,ARC 會分配一大塊內(nèi)存用來儲存實例的信息。內(nèi)存中會包含實例的類型信息官辈,以及這個實例所有相關屬性的值箱舞。此外遍坟,當實例不再被使用時,ARC會釋放實例所占用的內(nèi)存晴股,并讓釋放的內(nèi)存其他使用愿伴。這就確保了不再被使用的實例,不會一直占用內(nèi)存空間电湘。

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;

執(zhí)行結(jié)果:

xz is being initialized
xz is being deinitialized

類實例之間的循環(huán)強引用(Strong Reference Cycles Between Class Instances)

循環(huán)引用在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");

// 以下是核心引發(fā)的例子
john!.apartment = unit4A;
unit4A!.tenant = john;

john = nil;
unit4A = nil;

解決類實例之間的強循環(huán)引用(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 提供了兩種辦法用來解決你在使用類的屬性時所遇到的循環(huán)強引用問題:弱引用(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 銷毀被引用的實例官帘。這種行為阻止了引用變?yōu)檠h(huán)強引用。聲明屬性或者變量時昧谊,在前面加上weak關鍵字表明這是一個弱引用刽虹。

直接改寫一下上述的例子:

class Apartment {
    let unit: String;
    init(unit: String) { self.unit = unit; }
    weak var tenant: Person?; // 注意此處
    deinit { print("Apartment \(unit) is being deinitialized"); }
}

執(zhí)行結(jié)果:

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;

執(zhí)行結(jié)果:

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)");

執(zhí)行結(jié)果:

Canada's capital city is called Ottawa

閉包中的循環(huán)強引用(Strong Reference Cycles for Closures)

與OC中的block引起的循環(huán)強引用一致,直接看一下例子:

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;

執(zhí)行結(jié)果:

<p>hello, world</p>

疑問:

  • 為什么deinit函數(shù)沒有執(zhí)行分俯? 因為asHTML的閉包"捕獲"了self,同時asHTML的屬性持有了閉包的強引用肾筐。二者之間產(chǎn)生了循環(huán)強引用。

解決閉包引起的循環(huán)強引用(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.
  • 為解決閉包和類實例之間的循環(huán)強引用缸剪,可以定義閉包時同時定義捕獲列表作為閉包的一部分吗铐。

具體捕獲列表的語法參考如下:

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.
  • 將閉包內(nèi)的捕獲定義為無主引用,當閉包和捕獲的實例總是互相引用時并且總是同時銷毀時。相反的杏节,將閉包內(nèi)的捕獲定義為弱引用唬渗,當捕獲引用有時可能會是nil時。

上述的例子中奋渔,顯然無主引用可以解決上述閉包引起的循環(huán)強引用問題镊逝,具體代碼如下:

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;

執(zhí)行結(jié)果:

<p>hello, world</p>
p is being deinitialized
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市卒稳,隨后出現(xiàn)的幾起案子蹋半,更是在濱河造成了極大的恐慌,老刑警劉巖充坑,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件减江,死亡現(xiàn)場離奇詭異,居然都是意外死亡捻爷,警方通過查閱死者的電腦和手機辈灼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來也榄,“玉大人巡莹,你說我怎么就攤上這事√鹱希” “怎么了降宅?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長囚霸。 經(jīng)常有香客問我腰根,道長,這世上最難降的妖魔是什么拓型? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任额嘿,我火速辦了婚禮,結(jié)果婚禮上劣挫,老公的妹妹穿的比我還像新娘册养。我一直安慰自己,他們只是感情好压固,可當我...
    茶點故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布球拦。 她就那樣靜靜地躺著,像睡著了一般帐我。 火紅的嫁衣襯著肌膚如雪刘莹。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天焚刚,我揣著相機與錄音点弯,去河邊找鬼。 笑死矿咕,一個胖子當著我的面吹牛抢肛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播碳柱,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼捡絮,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了莲镣?” 一聲冷哼從身側(cè)響起福稳,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎瑞侮,沒想到半個月后的圆,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鼓拧,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年越妈,在試婚紗的時候發(fā)現(xiàn)自己被綠了季俩。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,567評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡梅掠,死狀恐怖酌住,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情阎抒,我是刑警寧澤酪我,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站且叁,受9級特大地震影響都哭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜谴古,卻給世界環(huán)境...
    茶點故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一质涛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧掰担,春花似錦汇陆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至勺疼,卻和暖如春教寂,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背执庐。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工酪耕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人轨淌。 一個月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓迂烁,卻偏偏與公主長得像,于是被迫代替她去往敵國和親递鹉。 傳聞我的和親對象是個殘疾皇子盟步,可洞房花燭夜當晚...
    茶點故事閱讀 45,585評論 2 359

推薦閱讀更多精彩內(nèi)容