Swift之旅_Language Guide4

接著看下面的幾個(gè)小節(jié),話說剩下的幾個(gè)小姐也忒特么長了吧~


Protocols

這一小節(jié)主要講解的是Swift中協(xié)議膏秫,超長的一節(jié)右遭。

  • Property Requirements

這里有點(diǎn)難翻譯,直接說吧缤削。這里說的是協(xié)議中定義屬性的時(shí)候窘哈,遵循協(xié)議的類型也要在其內(nèi)部定義該屬性,并且如果協(xié)議中屬性是gettable和settable的亭敢,則類型內(nèi)屬性必須是var的(不能是constant或者只讀)滚婉,如果協(xié)議中的屬性是gettable的,則類型內(nèi)可以是任何類型帅刀∪酶梗看一下代碼吧:

protocol FullyNamed {
    var fullName: String { set get }
}
struct Person: FullyNamed {
    var fullName: String
}
struct Animal: FullyNamed {
    //  編譯錯(cuò)誤: Type 'Animal' does not conform to protocol 'FullyNamed'
    //  必須是val
    let fullName: String
}
protocol FullyNamed {
    var fullName: String { get }
}
struct Person: FullyNamed {
    var fullName: String
}
struct Animal: FullyNamed {
    //  沒有錯(cuò)誤
    let fullName: String
}
  • Initializer Requirements

協(xié)議也可以定義構(gòu)造器,在遵循協(xié)議的類型中可以將這個(gè)構(gòu)造器實(shí)現(xiàn)成指定構(gòu)造器或者便利構(gòu)造器:

protocol SomeProtocol {
    init(someParameter: Int)
}
class OtherClass: SomeProtocol {
    required init(someParameter: Int) {
        // initializer implementation goes here
    }
}
class SomeClass: SomeProtocol {
    required convenience init(someParameter: Int) {
        // initializer implementation goes here
        self.init(test: 0)
    }
    init(test: Int) {
    }
}
  • Class-Only Protocols

可以在協(xié)議后面添加上AnyObject表示這個(gè)協(xié)議只能被類遵循

class Person: SomeClassOnlyProtocol {
}
struct Size: SomeClassOnlyProtocol {
    //報(bào)錯(cuò):Non-class type 'Size' cannot conform to class protocol 'SomeClassOnlyProtocol'
}
protocol SomeClassOnlyProtocol: AnyObject {
}
  • Checking for Protocol Conformance

也可以用is和as去操作協(xié)議

class Person: SomeClassOnlyProtocol {
    func test() {
        print("Hello")
    }
}
protocol SomeClassOnlyProtocol: AnyObject {
    func test()
}
let p = Person()
let p1: Any = p
print(p1 is SomeClassOnlyProtocol)
//  打印 true
(p1 as? SomeClassOnlyProtocol)?.test()
//  打印 Hello
  • Optional Protocol Requirements

和oc一樣扣溺,Swift協(xié)議中的屬性或者方法也是可以修飾成可以不實(shí)現(xiàn)的骇窍,但是Swift中比較奇怪一點(diǎn)。首先需要在協(xié)議前面加上@objc锥余,然后在可以選擇不實(shí)現(xiàn)的方法前面加上@objc optional腹纳,@objc在文檔中解釋是為了能夠在OC中調(diào)用,optional就是可選的意思嘛(ps:加上@objc之后該協(xié)議只能被Objective-C的類遵循驱犹,無法被結(jié)構(gòu)體和枚舉遵循)

class Person: SomeProtocol {
}
@objc protocol SomeProtocol {
    @objc optional func test()
}
struct test: SomeProtocol {
    //錯(cuò)誤: Non-class type 'test' cannot conform to class protocol 'SomeProtocol'
}
  • Protocol Extensions

也可以給協(xié)議擴(kuò)展方法嘲恍,不過這里不只是聲明方法,需要實(shí)現(xiàn)

class Person: SomeProtocol {
}
@objc protocol SomeProtocol {
}
extension SomeProtocol {
    func test() {
        print("hello")
    }
}
let p = Person()
p.test()
//打印 hello

Generics

這一小節(jié)主要講解的是Swift中泛型雄驹,依舊是長長的一節(jié)佃牛。

Generics are one of the most powerful features of Swift, and much of the Swift standard library is built with generic code. In fact, you’ve been using generics throughout the Language Guide, even if you didn’t realize it. For example, Swift’s Array and Dictionary types are both generic collections. You can create an array that holds Int values, or an array that holds String values, or indeed an array for any other type that can be created in Swift. Similarly, you can create a dictionary to store values of any specified type, and there are no limitations on what that type can be.

貌似泛型還是Swift的一大特性,并且SWIFT標(biāo)準(zhǔn)庫的大部分都是用泛型代碼構(gòu)建的医舆。數(shù)組字典其實(shí)都是泛型集合吁脱。

  • Generic Functions

來看看泛型的簡單應(yīng)用:
方法中的應(yīng)用

func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
}
var someInt = 3
var anotherInt = 107
swapTwoValues(&someInt, &anotherInt)
// someInt is now 107, and anotherInt is now 3
var someString = "hello"
var anotherString = "world"
swapTwoValues(&someString, &anotherString)
// someString is now "world", and anotherString is now "hello"

類型中的應(yīng)用

class Stack<Value> {
    var items = [Value]()
    subscript(index: Int) -> Value {
        return items[index]
    }
    func push(item: Value) {
        items.append(item)
    }
    func pop() {
        items.removeLast()
    }
}
let s = Stack<String>()
s.push(item: "Hello")
s.push(item: " ")
s.push(item: "World")
s.push(item: "!")
print(s[3])
  • Extending a Generic Type

給一個(gè)泛型擴(kuò)展

extension Stack {
    var top: Value? {
        return items.isEmpty ? nil : items[items.count-1]
    }
}
  • Type Constraint Syntax

也可以對(duì)一個(gè)泛型進(jìn)行約束:

func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
    // function body goes here
}

這個(gè)函數(shù)中T必須是SomeClass的子類桑涎,U必須遵循SomeProtocol協(xié)議

  • Associated Types

關(guān)聯(lián)類型。哇兼贡,這段話真的是很難翻譯啊攻冷。百度好多都是直接抄文檔的啥解釋也沒有,看了很久才搞懂一點(diǎn)遍希。associatedtype修飾的東西可以代指一個(gè)類型等曼,文檔中也有例子,不過我覺得還是簡化一下看的更明白:

protocol Container {
    associatedtype Item
    mutating func append(_ item: Item)
}
struct Stack<Element>: Container {
    var items = [Element]()
    mutating func append(_ item: Element) {
        items.append(item)
    }
}
struct Queue<Value>: Container {
    var items = [Value]()
    mutating func append(_ item: Value) {
        items.append(item)
    }
}

這里associatedtype后面跟的其實(shí)相當(dāng)于是一個(gè)泛型凿蒜,接著Stack和Queue遵行Container協(xié)議禁谦,在Stack中Item代指Element類型,而在Queue中代指Value類型废封。

  • Adding Constraints to an Associated Type
protocol Container {
    associatedtype Item: Equatable
    mutating func append(_ item: Item)
    var count: Int { get }
    subscript(i: Int) -> Item { get }
}
  • Extensions with a Generic Where Clause

extension中使用泛型限制條款

extension Stack where Element: Equatable {
    func isTop(_ item: Element) -> Bool {
        guard let topItem = items.last else {
            return false
        }
        return topItem == item
    }
}

這里的Stack集合中的元素必須是遵循Equatable協(xié)議的,也就是說只有當(dāng)Stack集合中的元素遵循了Equatable協(xié)議州泊,該集合才能獲得isTop這個(gè)擴(kuò)展方法。后面也有提到關(guān)聯(lián)類型以及下標(biāo)中都可以這樣使用漂洋。


Automatic Reference Counting

這一小節(jié)主要講解的是Swift中的內(nèi)存管理遥皂。和oc一樣,swift也是自動(dòng)管理內(nèi)存刽漂,但是仍然存在一些情況演训,需要特殊處理。

  • Strong Reference Cycles Between Class Instances

類實(shí)例之間的強(qiáng)引用循環(huán),這里介紹了一個(gè)循環(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")
john!.apartment = unit4A
unit4A!.tenant = John
john = nil
unit4A = nil

結(jié)果運(yùn)行的時(shí)候2個(gè)實(shí)例對(duì)象的析構(gòu)函數(shù)都不會(huì)被調(diào)用,說明2者都沒有被成功釋放庭猩。官方文檔中3張圖很形象的描述了這個(gè)過程窟她。
首先john和unit4A指針分別指向一個(gè)實(shí)例對(duì)象
接著john!.apartment指針指向unit4A所指向的實(shí)例對(duì)象,unit4A!.tenant指向john所指向的實(shí)例對(duì)象
john和unit4A指向nil蔼水,但是因?yàn)?個(gè)實(shí)例對(duì)象之間都還存在強(qiáng)引用震糖,所以內(nèi)存并沒有成功釋放。
  • Resolving Strong Reference Cycles Between Class Instances

有兩種解決方法:

1.Weak References

和oc的弱引用幾乎一樣徙缴,需要注意的是因?yàn)槿跻迷诔绦蜻\(yùn)行過程中可能會(huì)被設(shè)置為nil试伙,所以weak修飾的通常是變量而不是常量嘁信。上面代碼需要改成如下:

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 }
    weak 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

對(duì)于這段代碼文檔中也有圖片形容.
釋放之前的內(nèi)存圖
john = nil之后的內(nèi)存圖
unit4A = nil之后的內(nèi)存圖

不過我試過如果John不設(shè)置成nil于样,unit4A也是不能正常釋放,并且john!.apartment也不為nil潘靖,依舊指向著那塊內(nèi)存空間穿剖。

2.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.

Unowned和弱引用一樣的地方是對(duì)實(shí)例對(duì)象沒有保持一個(gè)強(qiáng)引用,不同的是Unowned是用在當(dāng)其他實(shí)例對(duì)象擁有相同或者更長的生命周期的時(shí)候卦溢。
unowned修飾的實(shí)例對(duì)象一直會(huì)有一個(gè)值糊余,所以ARC從不將它設(shè)置成nil秀又,這意味著unowned修飾的實(shí)例對(duì)象通常被定義成非可選類型。
使用如下:

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 Appleseed")
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: John!)
john = nil

對(duì)應(yīng)的內(nèi)存圖:
john = nil之前
john = nil之后

文檔上沒有第三張圖了贬芥,不過這里也容易看出下面的內(nèi)存情況吐辙,因?yàn)镃ustomer instance沒有string指針指著了,所以會(huì)被釋放掉蘸劈,接著CreditCard instance也沒有strong指針指著昏苏,也會(huì)被釋放。

  • Strong Reference Cycles for Closures

在閉包中的強(qiáng)引用威沫,oc中也存在這樣的情況贤惯,當(dāng)你沒做任何處理直接在閉包中通過self.someProperty去訪問屬性或者 self.someMethod()去調(diào)用方法就會(huì)對(duì)self進(jìn)行一次"捕獲",從而造成循環(huán)引用棒掠,使self不能正常釋放孵构。
錯(cuò)誤代碼如下:

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())
// Prints "<p>hello, world</p>"
paragraph = nil

對(duì)應(yīng)的內(nèi)存圖:

文檔中沒有給出paragraph = nil的內(nèi)存圖,不過paragraph = nil之后烟很,閉包仍然引用著實(shí)例對(duì)象颈墅,所以這個(gè)實(shí)例對(duì)象不能成功釋放,這里也可以看出閉包是引用類型的溯职。

  • Resolving Strong Reference Cycles for Closures

解決閉包的強(qiáng)引用

class HTMLElement {
    let name: String
    let text: String?
    lazy var asHTML: () -> String = {
        [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())
// Prints "<p>hello, world</p>"
paragraph = nil
// Prints "p is being deinitialized"

paragraph = nil之前的內(nèi)存圖如下:

這樣子就能解決閉包強(qiáng)引用的問題了精盅。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市谜酒,隨后出現(xiàn)的幾起案子叹俏,更是在濱河造成了極大的恐慌,老刑警劉巖僻族,帶你破解...
    沈念sama閱讀 216,997評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件粘驰,死亡現(xiàn)場離奇詭異,居然都是意外死亡述么,警方通過查閱死者的電腦和手機(jī)蝌数,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來度秘,“玉大人顶伞,你說我怎么就攤上這事〗J幔” “怎么了唆貌?”我有些...
    開封第一講書人閱讀 163,359評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長垢乙。 經(jīng)常有香客問我锨咙,道長,這世上最難降的妖魔是什么追逮? 我笑而不...
    開封第一講書人閱讀 58,309評(píng)論 1 292
  • 正文 為了忘掉前任酪刀,我火速辦了婚禮粹舵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘骂倘。我一直安慰自己眼滤,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評(píng)論 6 390
  • 文/花漫 我一把揭開白布历涝。 她就那樣靜靜地躺著柠偶,像睡著了一般。 火紅的嫁衣襯著肌膚如雪睬关。 梳的紋絲不亂的頭發(fā)上小槐,一...
    開封第一講書人閱讀 51,258評(píng)論 1 300
  • 那天力穗,我揣著相機(jī)與錄音赔癌,去河邊找鬼哀卫。 笑死,一個(gè)胖子當(dāng)著我的面吹牛丐箩,可吹牛的內(nèi)容都是我干的摇邦。 我是一名探鬼主播,決...
    沈念sama閱讀 40,122評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼屎勘,長吁一口氣:“原來是場噩夢啊……” “哼施籍!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起概漱,我...
    開封第一講書人閱讀 38,970評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤丑慎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后瓤摧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體竿裂,經(jīng)...
    沈念sama閱讀 45,403評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評(píng)論 3 334
  • 正文 我和宋清朗相戀三年照弥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了腻异。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,769評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡这揣,死狀恐怖悔常,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情给赞,我是刑警寧澤机打,帶...
    沈念sama閱讀 35,464評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站塞俱,受9級(jí)特大地震影響姐帚,放射性物質(zhì)發(fā)生泄漏吏垮。R本人自食惡果不足惜障涯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評(píng)論 3 327
  • 文/蒙蒙 一罐旗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧唯蝶,春花似錦九秀、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至征字,卻和暖如春都弹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背匙姜。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評(píng)論 1 269
  • 我被黑心中介騙來泰國打工畅厢, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人氮昧。 一個(gè)月前我還...
    沈念sama閱讀 47,831評(píng)論 2 370
  • 正文 我出身青樓框杜,卻偏偏與公主長得像,于是被迫代替她去往敵國和親袖肥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子咪辱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評(píng)論 2 354

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