Swift (八)

泛型

泛型版本的函數(shù)使用占位符類型名(這里叫做?T?)贞间,而不是?實(shí)際類型名(例如?Int茉帅、String?或?Double)瘫絮,占位符類型名并不關(guān)心?T?具體的類型齐佳,但它要求?a?和b?必須是相同的類型私恬,T?的實(shí)際類型由每次調(diào)用?swapTwoValues(_:_:)?來決定。

泛型函數(shù)和非泛型函數(shù)的另外一個(gè)不同之處在于這個(gè)泛型函數(shù)名(swapTwoValues(_:_:))后面跟著占位類型名(T)炼吴,并用尖括號(hào)括起來(<T>)本鸣。這個(gè)尖括號(hào)告訴 Swift 那個(gè)?T?是?swapTwoValues(_:_:)?函數(shù)定義內(nèi)的一個(gè)占位類型名,因此 Swift 不會(huì)去查找名為?T的實(shí)際類型硅蹦。

自動(dòng)引用計(jì)數(shù):引用計(jì)數(shù)僅僅應(yīng)用于類的實(shí)例荣德。結(jié)構(gòu)體和枚舉類型是值類型闷煤,不是引用類型,也不是通過引用的方式存儲(chǔ)和傳遞

自動(dòng)引用計(jì)數(shù)的工作機(jī)制 :當(dāng)你每次創(chuàng)建一個(gè)類的新的實(shí)例的時(shí)候涮瞻,ARC 會(huì)分配一塊內(nèi)存來儲(chǔ)存該實(shí)例信息鲤拿。內(nèi)存中會(huì)包含實(shí)例的類型信息,以及這個(gè)實(shí)例所有相關(guān)的存儲(chǔ)型屬性的值署咽。此外近顷,當(dāng)實(shí)例不再被使用時(shí),ARC 釋放實(shí)例所占用的內(nèi)存艇抠,并讓釋放的內(nèi)存能挪作他用幕庐。這確保了不再被使用的實(shí)例,不會(huì)一直占用內(nèi)存空間家淤。

然而异剥,當(dāng) ARC 收回和釋放了正在被使用中的實(shí)例,該實(shí)例的屬性和方法將不能再被訪問和調(diào)用絮重。實(shí)際上冤寿,如果你試圖訪問這個(gè)實(shí)例,你的應(yīng)用程序很可能會(huì)崩潰青伤。

為了確保使用中的實(shí)例不會(huì)被銷毀督怜,ARC 會(huì)跟蹤和計(jì)算每一個(gè)實(shí)例正在被多少屬性,常量和變量所引用狠角。哪怕實(shí)例的引用數(shù)為 1号杠,ARC 都不會(huì)銷毀這個(gè)實(shí)例。

為了使上述成為可能丰歌,無論你將實(shí)例賦值給屬性姨蟋、常量或變量,它們都會(huì)創(chuàng)建此實(shí)例的強(qiáng)引用立帖。之所以稱之為“強(qiáng)”引用眼溶,是因?yàn)樗鼤?huì)將實(shí)例牢牢地保持住,只要強(qiáng)引用還在晓勇,實(shí)例是不允許被銷毀的堂飞。

弱引用:當(dāng) ARC 設(shè)置弱引用為?nil?時(shí),屬性觀察不會(huì)被觸發(fā)绑咱。

解決實(shí)例之間的循環(huán)強(qiáng)引用:Swift 提供了兩種辦法用來解決你在使用類的屬性時(shí)所遇到的循環(huán)強(qiáng)引用問題:弱引用(weak reference)和無主引用(unowned reference)绰筛。

弱引用:弱引用不會(huì)對其引用的實(shí)例保持強(qiáng)引用,因而不會(huì)阻止 ARC 銷毀被引用的實(shí)例描融。這個(gè)特性阻止了引用變?yōu)檠h(huán)強(qiáng)引用别智。聲明屬性或者變量時(shí),在前面加上?weak?關(guān)鍵字表明這是一個(gè)弱引用稼稿。因?yàn)槿跻貌粫?huì)保持所引用的實(shí)例薄榛,即使引用存在,實(shí)例也有可能被銷毀让歼。因此敞恋,ARC 會(huì)在引用的實(shí)例被銷毀后自動(dòng)將其弱引用賦值為?nil。并且因?yàn)槿跻眯枰谶\(yùn)行時(shí)允許被賦值為?nil谋右,所以它們會(huì)被定義為可選類型變量硬猫,而不是常量。

無主引用:和弱引用類似改执,無主引用不會(huì)牢牢保持住引用的實(shí)例啸蜜。和弱引用不同的是,無主引用在其他實(shí)例有相同或者更長的生命周期時(shí)使用辈挂。你可以在聲明屬性或者變量時(shí)衬横,在前面加上關(guān)鍵字?unowned?表示這是一個(gè)無主引用。

無主引用通常都被期望擁有值终蒂。不過 ARC 無法在實(shí)例被銷毀后將無主引用設(shè)為?nil蜂林,因?yàn)榉强蛇x類型的變量不允許被賦值為?nil。

重點(diǎn)

使用無主引用拇泣,你必須確保引用始終指向一個(gè)未銷毀的實(shí)例噪叙。

如果你試圖在實(shí)例被銷毀后,訪問該實(shí)例的無主引用霉翔,會(huì)觸發(fā)運(yùn)行時(shí)錯(cuò)誤睁蕾。


無主引用和隱式解包可選值屬性?

上面弱引用和無主引用的例子涵蓋了兩種常用的需要打破循環(huán)強(qiáng)引用的場景。

Person?和?Apartment?的例子展示了兩個(gè)屬性的值都允許為?nil债朵,并會(huì)潛在的產(chǎn)生循環(huán)強(qiáng)引用子眶。這種場景最適合用弱引用來解決。

Customer?和?CreditCard?的例子展示了一個(gè)屬性的值允許為?nil葱弟,而另一個(gè)屬性的值不允許為?nil壹店,這也可能會(huì)產(chǎn)生循環(huán)強(qiáng)引用。這種場景最適合通過無主引用來解決芝加。

然而硅卢,存在著第三種場景,在這種場景中藏杖,兩個(gè)屬性都必須有值将塑,并且初始化完成后永遠(yuǎn)不會(huì)為?nil。在這種場景中蝌麸,需要一個(gè)類使用無主屬性点寥,而另外一個(gè)類使用隱式解包可選值屬性。

閉包的循環(huán)強(qiáng)引用:循環(huán)強(qiáng)引用還會(huì)發(fā)生在當(dāng)你將一個(gè)閉包賦值給類實(shí)例的某個(gè)屬性来吩,并且這個(gè)閉包體中又使用了這個(gè)類實(shí)例時(shí)敢辩。這個(gè)閉包體中可能訪問了實(shí)例的某個(gè)屬性蔽莱,例如?self.someProperty,或者閉包中調(diào)用了實(shí)例的某個(gè)方法戚长,例如?self.someMethod()盗冷。這兩種情況都導(dǎo)致了閉包“捕獲”self,從而產(chǎn)生了循環(huán)強(qiáng)引用同廉。

循環(huán)強(qiáng)引用的產(chǎn)生仪糖,是因?yàn)殚]包和類相似,都是引用類型迫肖。當(dāng)你把一個(gè)閉包賦值給某個(gè)屬性時(shí)锅劝,你是將這個(gè)閉包的引用賦值給了屬性。實(shí)質(zhì)上蟆湖,這跟之前的問題是一樣的——兩個(gè)強(qiáng)引用讓彼此一直有效故爵。但是,和兩個(gè)類實(shí)例不同帐姻,這次一個(gè)是類實(shí)例稠集,另一個(gè)是閉包。

Swift 提供了一種優(yōu)雅的方法來解決這個(gè)問題饥瓷,稱之為?閉包捕獲列表(closure capture list)剥纷。

閉包的循環(huán)強(qiáng)引用:循環(huán)強(qiáng)引用還會(huì)發(fā)生在當(dāng)你將一個(gè)閉包賦值給類實(shí)例的某個(gè)屬性,并且這個(gè)閉包體中又使用了這個(gè)類實(shí)例時(shí)呢铆。這個(gè)閉包體中可能訪問了實(shí)例的某個(gè)屬性晦鞋,例如?self.someProperty,或者閉包中調(diào)用了實(shí)例的某個(gè)方法棺克,例如?self.someMethod()悠垛。這兩種情況都導(dǎo)致了閉包“捕獲”self,從而產(chǎn)生了循環(huán)強(qiáng)引用娜谊。

解決閉包的循環(huán)強(qiáng)引用:在定義閉包時(shí)同時(shí)定義捕獲列表作為閉包的一部分确买,通過這種方式可以解決閉包和類實(shí)例之間的循環(huán)強(qiáng)引用。捕獲列表定義了閉包體內(nèi)捕獲一個(gè)或者多個(gè)引用類型的規(guī)則纱皆。跟解決兩個(gè)類實(shí)例間的循環(huán)強(qiáng)引用一樣湾趾,聲明每個(gè)捕獲的引用為弱引用或無主引用,而不是強(qiáng)引用派草。應(yīng)當(dāng)根據(jù)代碼關(guān)系來決定使用弱引用還是無主引用搀缠。

注意

Swift 有如下要求:只要在閉包內(nèi)使用?self?的成員,就要用?self.someProperty?或者?self.someMethod()(而不只是?someProperty?或?someMethod())近迁。這提醒你可能會(huì)一不小心就捕獲了?self艺普。

定義捕獲列表:如果閉包有參數(shù)列表和返回類型,把捕獲列表放在它們前面:

如果閉包沒有指明參數(shù)列表或者返回類型,它們會(huì)通過上下文推斷歧譬,那么可以把捕獲列表和關(guān)鍵字?in放在閉包最開始的地方:

弱引用和無主引用:在閉包和捕獲的實(shí)例總是互相引用并且總是同時(shí)銷毀時(shí)岸浑,將閉包內(nèi)的捕獲定義為?無主引用。

相反的瑰步,在被捕獲的引用可能會(huì)變?yōu)?nil?時(shí)助琐,將閉包內(nèi)的捕獲定義為?弱引用。弱引用總是可選類型面氓,并且當(dāng)引用的實(shí)例被銷毀后,弱引用的值會(huì)自動(dòng)置為?nil蛆橡。這使我們可以在閉包體內(nèi)檢查它們是否存在舌界。

注意

如果被捕獲的引用絕對不會(huì)變?yōu)?nil,應(yīng)該用無主引用泰演,而不是弱引用呻拌。


內(nèi)存安全

默認(rèn)情況下,Swift 會(huì)阻止你代碼里不安全的行為睦焕。例如藐握,Swift 會(huì)保證變量在使用之前就完成初始化,在內(nèi)存被回收之后就無法被訪問垃喊,并且數(shù)組的索引會(huì)做越界檢查猾普。

理解內(nèi)存訪問沖突

內(nèi)存訪問的沖突會(huì)發(fā)生在你的代碼嘗試同時(shí)訪問同一個(gè)存儲(chǔ)地址的時(shí)侯。同一個(gè)存儲(chǔ)地址的多個(gè)訪問同時(shí)發(fā)生會(huì)造成不可預(yù)計(jì)或不一致的行為本谜。在 Swift 里初家,有很多修改值的行為都會(huì)持續(xù)好幾行代碼,在修改值的過程中進(jìn)行訪問是有可能發(fā)生的乌助。

內(nèi)存訪問性質(zhì)?

內(nèi)存訪問沖突時(shí)溜在,要考慮內(nèi)存訪問上下文中的這三個(gè)性質(zhì):訪問是讀還是寫,訪問的時(shí)長他托,以及被訪問的存儲(chǔ)地址掖肋。特別是,沖突會(huì)發(fā)生在當(dāng)你有兩個(gè)訪問符合下列的情況:

1.至少有一個(gè)是寫訪問

2.它們訪問的是同一個(gè)存儲(chǔ)地址

3.它們的訪問在時(shí)間線上部分重疊

正常來說赏参,兩個(gè)瞬時(shí)訪問是不可能同時(shí)發(fā)生的志笼。大多數(shù)內(nèi)存訪問都是瞬時(shí)的。然而登刺,有幾種被稱為長期訪問的內(nèi)存訪問方式籽腕,會(huì)在別的代碼執(zhí)行時(shí)持續(xù)進(jìn)行。瞬時(shí)訪問和長期訪問的區(qū)別在于別的代碼有沒有可能在訪問期間同時(shí)訪問纸俭,也就是在時(shí)間線上的重疊皇耗。一個(gè)長期訪問可以被別的長期訪問或瞬時(shí)訪問重疊。

重疊的訪問主要出現(xiàn)在使用 in-out 參數(shù)的函數(shù)和方法或者結(jié)構(gòu)體的 mutating 方法里揍很。Swift 代碼里典型的長期訪問會(huì)在后面進(jìn)行討論郎楼。

In-Out 參數(shù)的訪問沖突:一個(gè)函數(shù)會(huì)對它所有的 in-out 參數(shù)進(jìn)行長期寫訪問万伤。in-out 參數(shù)的寫訪問會(huì)在所有非 in-out 參數(shù)處理完之后開始,直到函數(shù)執(zhí)行完畢為止呜袁。如果有多個(gè) in-out 參數(shù)敌买,則寫訪問開始的順序與參數(shù)的順序一致。

解決這個(gè)沖突的一種方式阶界,是顯示拷貝一份?

屬性的訪問沖突

限制結(jié)構(gòu)體屬性的重疊訪問對于保證內(nèi)存安全不是必要的虹钮。保證內(nèi)存安全是必要的,但因?yàn)樵L問獨(dú)占權(quán)的要求比內(nèi)存安全還要更嚴(yán)格——意味著即使有些代碼違反了訪問獨(dú)占權(quán)的原則膘融,也是內(nèi)存安全的芙粱,所以如果編譯器可以保證這種非專屬的訪問是安全的,那 Swift 就會(huì)允許這種行為的代碼運(yùn)行氧映。特別是當(dāng)你遵循下面的原則時(shí)春畔,它可以保證結(jié)構(gòu)體屬性的重疊訪問是安全的:

你訪問的是實(shí)例的存儲(chǔ)屬性,而不是計(jì)算屬性或類的屬性

結(jié)構(gòu)體是本地變量的值岛都,而非全局變量

結(jié)構(gòu)體要么沒有被閉包捕獲律姨,要么只被非逃逸閉包捕獲了

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市臼疫,隨后出現(xiàn)的幾起案子择份,更是在濱河造成了極大的恐慌,老刑警劉巖多矮,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缓淹,死亡現(xiàn)場離奇詭異,居然都是意外死亡塔逃,警方通過查閱死者的電腦和手機(jī)讯壶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來湾盗,“玉大人伏蚊,你說我怎么就攤上這事「穹啵” “怎么了躏吊?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長帐萎。 經(jīng)常有香客問我比伏,道長,這世上最難降的妖魔是什么疆导? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任赁项,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘悠菜。我一直安慰自己舰攒,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布悔醋。 她就那樣靜靜地躺著摩窃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪芬骄。 梳的紋絲不亂的頭發(fā)上猾愿,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天,我揣著相機(jī)與錄音账阻,去河邊找鬼匪蟀。 笑死,一個(gè)胖子當(dāng)著我的面吹牛宰僧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播观挎,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼琴儿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了嘁捷?” 一聲冷哼從身側(cè)響起造成,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎雄嚣,沒想到半個(gè)月后晒屎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡缓升,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年鼓鲁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片港谊。...
    茶點(diǎn)故事閱讀 38,100評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡骇吭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出歧寺,到底是詐尸還是另有隱情燥狰,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布斜筐,位于F島的核電站龙致,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏顷链。R本人自食惡果不足惜目代,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧像啼,春花似錦俘闯、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至僧诚,卻和暖如春遮婶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背湖笨。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工旗扑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人慈省。 一個(gè)月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓臀防,卻偏偏與公主長得像,于是被迫代替她去往敵國和親边败。 傳聞我的和親對象是個(gè)殘疾皇子袱衷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評論 2 345

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