Swift-鏈?zhǔn)介_發(fā)思想

一. 什么是鏈?zhǔn)剑?/h2>

可以連續(xù)不斷地進?方法調(diào)?用的一種語法形式俊扭。

二. 探究鏈?zhǔn)降氖褂门c實現(xiàn)本質(zhì)


示例1:打豆豆

有位科學(xué)家到了南極妨退,碰到一群企鵝赠涮。他問其中一個:“你每天都干什么呀华弓?”那企鵝說:“吃飯睡覺打豆豆芭挽』希”
他又問另一個:“你每天都干什么呀?”那企鵝也源說:“吃飯睡覺打豆豆袜爪∪涑茫”
他問了許多許多的企鵝,都說:“吃飯睡覺打豆豆辛馆“陈”
后來他碰到了一只小企鵝,很可愛的樣子昙篙,就問它:“小朋友腊状,你每天都干什么呀?”小企鵝說:“吃飯睡覺瓢对∈僮茫”科學(xué)家一愣,隨即問到:“你怎么不打豆豆呀硕蛹?”小企鵝委屈的說:“因為我就是豆豆醇疼∷恫ⅲ”

思考一下如何用代碼表述這些企鵝每天都做了什么?

普通的實現(xiàn)方式

// 其他企鵝的一天
let otherPenguin = Penguin()
otherPenguin.eat()
otherPenguin.sleep()
otherPenguin.strike(penguin: "豆豆")
print(otherPenguin.description)
// ->吃飯->睡覺->打豆豆

使用鏈?zhǔn)綄崿F(xiàn)

let longDay = Penguin.start { (make) in
    make.eat().sleep().strike("豆豆")
}
print(penguin.description)
// ->吃飯->睡覺->打豆豆
// 企鵝類
class Penguin {
    fileprivate var description: String = ""

    static func start(block: (Penguin) -> Void) -> String {
        let penguin = Penguin()
        block(penguin)
        return penguin.description
    }
}

extension Penguin {
    @discardableResult
    func eat() -> Self {
        description += "->吃飯"
        return self
    }
    
    @discardableResult
    func sleep() -> Penguin {
        description += "->睡覺"
        return self
    }
    
    @discardableResult
    func strike(_ name: String) -> Penguin {
        description += "->打\(name)"
        return self
    }
}

通過這個示例可以發(fā)現(xiàn)鏈?zhǔn)奖磉_優(yōu)點:精簡代碼,提升代碼的閱讀性秧荆。

示例2:實現(xiàn)簡單的計算器功能

let result = Calculator.begin { (maker) in
   maker.add(n: 2).subtract(n: 2).add(n: 3).divide(n: 0)
}
print(result)
public class Calculator {
    public static func begin(caculateBlock:(CaculateMaker) -> ()) -> Double {
        let caculator = CaculateMaker()
        caculateBlock(caculator)
        return caculator.result
    }
}

public class CaculateMaker {
    
    public var result: Double = 0
    
    /// 加法
    @discardableResult
    public func add(n: Double) -> CaculateMaker {
        result += n
        return self
    }
    
    /// 減法
    @discardableResult
    public func subtract(n: Double) -> CaculateMaker {
        result -= n
        return self
    }
    
    /// 乘法
    @discardableResult
    public func multiply(n: Double) -> CaculateMaker {
        result *= n
        return self
    }

    /// 除法
    @discardableResult
    public func divide(n: Double) -> CaculateMaker? {
        
        if n == 0 {
            result = 0
        } else {
            result /= n
        }
        return self
    }
}

示例3: Swift的高級函數(shù)的使用

實現(xiàn)對數(shù)組去nil處理倔毙,并對數(shù)組排序的需求

let countArray = [1, 2, 4, 5, nil, 3]

let resultArr = countArray.compactMap { $0 }.sorted(by: <)
print(resultArr)
// [1, 2, 3, 4, 5]

查看使用的這兩個系統(tǒng)方法的源碼

public struct Array<Element> {
    @inlinable public func compactMap<ElementOfResult>(_ transform: (Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]
    
    @inlinable public func sorted(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> [Element]
}

發(fā)現(xiàn)compactMapsorted函數(shù)都返回一個數(shù)組類型,滿足鏈?zhǔn)秸{(diào)用的條件乙濒。

可以將上面的鏈?zhǔn)讲鸾鉃?/p>

let arr1 = countArray.compactMap { $0 }
let arr2 = arr1.sorted(by: < )

通過示例2可以發(fā)現(xiàn)鏈?zhǔn)降牧硪粋€優(yōu)點: 減少中間變量陕赃。

示例4:鏈?zhǔn)経I的使用

鏈?zhǔn)経I的介紹

移動端的開發(fā)工作離不開對UI的操作(包含UI對象的聲明,UI對象的屬性配置颁股,UI對象的添加么库,UI對象的約束布局等操作步驟)。這些操作需要大量的代碼來實現(xiàn)甘有,如果對代碼書寫規(guī)范不嚴(yán)格遵守的話诉儒,相關(guān)代碼就有可能分散在文件的各個地方,影響代碼的整體結(jié)構(gòu)性和閱讀性亏掀。我們可以用鏈?zhǔn)剿枷雭斫鉀Q這樣的問題忱反。

let _ = UILabel()
    .adhere(toSuperView: view)
    .layout (snapKitMaker: { (make) in
        make.top.equalToSuperview().offset(80)
        make.centerX.equalToSuperview()
    })
    .config ({(label) in
        label.backgroundColor = UIColor.clear
        label.font = UIFont.systemFont(ofSize: 20)
        label.textColor = UIColor.darkGray
        label.text = "Label"
    })

該段代碼實現(xiàn)了四個功能:

  • 初始化UILabel類型的對象
  • 將該對象添加到父視圖上
  • 給這個對象添加約束布局
  • 給這個對象設(shè)置屬性

通過鏈?zhǔn)?/code>這種實現(xiàn)方式,把UI的相關(guān)的代碼都寫在一起滤愕,方便管理和維護温算,極大的提升了代碼的可讀性。

通過命名空間式擴展避免命名沖突

前面我們給這些UIKit的類通過擴展的形式添加了這些方法 adhere , layout , config 间影,萬一以后蘋果也使用了同樣的方法命名注竿,我們就只能改方法的命名,非常不友好宇智。

SnapKit 前幾個版本的是通過添加 snp_ 前綴的方式用來區(qū)分的蔓搞。

view.snp_makeConstraints { (make) in
}

現(xiàn)在主流的Swift三方庫都支持采用命名空間式擴展

view.snp.makeConstraints { (make) in
}

所以我們的鏈?zhǔn)経I可以改成這樣

let _ = UILabel()
    .bt.add(toSuperView: testLabel)
    .bt.layout ({
        $0.center.equalToSuperview()
        $0.width.height.equalTo(100)
    })
    .bt.config(config)
    .bt.config ({
        $0.backgroundColor = UIColor.green
    })

關(guān)于鏈?zhǔn)経I的其他一些說明

我們的方法public func config(_ config: (T) -> Void) -> T。config 方法接收一個閉包作為參數(shù)随橘,所以也可以這樣用喂分,增強復(fù)用性:

//  寫一個配置的閉包
let config = {(label: UILabel) in
    label.backgroundColor = UIColor.red
    label.font = UIFont.systemFont(ofSize: 14)
    label.text = "label"
    label.textColor = UIColor.white
}

let testLabel = UILabel()
    .bt.add(toSuperView: view)
    .bt.layout ({
        $0.edges.equalToSuperview()
    })
    .bt.config(config)

let _ = UILabel()
    .bt.add(toSuperView: testLabel)
    .bt.layout ({
        $0.center.equalToSuperview()
        $0.width.height.equalTo(100)
    })
    .bt.config(config)
    // 差異化設(shè)置通配屬性
    .bt.config ({
        $0.backgroundColor = UIColor.green
    })

用鏈?zhǔn)経I的方式把UI相關(guān)的代碼都強制寫一起了,是不是為之后的更新代碼不便呢?

// 更新某一個需要更新的屬性配置
testLabel.bt.config {
    $0.textColor = UIColor.orange
}

// 當(dāng)然這樣也是可以的
testLabel.text = "改變紅色為橘色"

鏈?zhǔn)経I實現(xiàn)源碼

public protocol NamespaceWrappable {
    associatedtype BTWrapperType
    var bt: BTWrapperType { get }
    static var bt: BTWrapperType.Type { get }
}

public extension NamespaceWrappable {
    var bt: NamespaceWrapper<Self> {
        return NamespaceWrapper(value: self)
    }

    static var bt: NamespaceWrapper<Self>.Type {
        return NamespaceWrapper.self
    }
}

public struct NamespaceWrapper<T> {
    public let wrappedValue: T
    public init(value: T) {
        self.wrappedValue = value
    }
}
import SnapKit

extension NamespaceWrapper where T: UIView {

    /// 添加在視圖
    /// - Parameter toSuperView: 父視圖
    public func add(toSuperView: UIView) -> T {
        toSuperView.addSubview(wrappedValue)
        return wrappedValue
    }
    
    
    @discardableResult
    public func config(_ config: (T) -> Void) -> T {
        config(wrappedValue)
        return wrappedValue
    }

    @discardableResult
    public func layout(_ snapKitMaker: (ConstraintMaker) -> Void) -> T {
        wrappedValue.snp.makeConstraints { (make) in
            snapKitMaker(make)
        }
        return wrappedValue
    }
}

三. 階段性總結(jié)


通過以上的說明和幾個簡單的示例机蔗,發(fā)現(xiàn)鏈?zhǔn)骄哂幸韵聨讉€特點:

  • 代碼簡潔
  • 高復(fù)用性
  • 高可讀性
  • 減少中間變量

四. 鏈?zhǔn)秸{(diào)用的安全性


先來看一段約束布局

testLabel.snp.makeConstraints { (make) in
    make.width.equalTo(view.snp.left)
}

此段約束明顯是有問題的蒲祈,但是在編譯期不會報錯,只有真正運行到此處才會報錯: 布局屬性的配對無效.
: 'NSLayoutConstraint for <UILabel>: Invalid pairing of layout attributes.'
往往這種潛在問題都是致命的萝嘁。接下來我們探究如何避免這種情況梆掸?

我們嘗試用代碼實現(xiàn)一下這個要求:

有句網(wǎng)絡(luò)名言是這樣的“同性才是真愛,異性只為繁殖后代”

實現(xiàn)一個Love的協(xié)議牙言,聲明 ManWomen 兩個類酸钦,并遵守該協(xié)議。

protocol Love { }

class Man: Love { }

class Women: Love { }

extension Love {
    var man: Man {
        return Man()
    }
    
    var women: Women {
        return Women()
    }
}

struct Validation {
    static func trueLove<T: Love>(left: T, right: T)  {
        print("存在真愛")
    }
}
? Cannot invoke 'trueLove' with an argument list of type '(left: Man, right: Women)'
Validation.trueLove(left: man, right: women) 
Validation.trueLove(left: man, right: man.women) 
?
Validation.trueLove(left: man, right: man)

雖然中間一個是man 一個是 women 咱枉,但是鏈?zhǔn)降淖詈笠粋€對象類型是相同的卑硫。所以可以通過編譯徒恋。顯然不能滿足我們的需求。

?運行能否成功欢伏?答案是肯定的入挣。但是不符合我們的要求。
Validation.trueLove(left: man. man. man, right: man.women.man)

使用泛型約束繼續(xù)實現(xiàn)

protocol Love { }

class Man<X>: Love { }

class Women<X>: Love { }

extension Love {
    var man: Man<Self> {
        return Man()
    }
    
    var women: Women<Self> {
        return Women()
    }
}

struct Validation {
    static func trueLove<T: Love>(left: T, right: T)  {
        print("存在真愛")
    }
}

最終使用的時候是這樣的

let man = Man<Any>()
let women = Women<Any>()
? 
Validation.trueLove(left: man, right: man.women.man)
Validation.trueLove(left: women.man, right: man.women.man)
?
Validation.trueLove(left: man, right: man)
Validation.trueLove(left: women.man, right: women.man)

可以通過這樣的方式硝拧,降低程序的錯誤率径筏,提高代碼的安全性

五. 可選鏈?zhǔn)秸{(diào)用


1. 概念

可選鏈?zhǔn)秸{(diào)用是指當(dāng)前值為可選類型情況下障陶,對當(dāng)前值執(zhí)行(獲取屬性滋恬、方法或下標(biāo))操作,會出現(xiàn)以下情況

  • 如果當(dāng)前的可選值為nil咸这,調(diào)用失敗并返回nil夷恍;
  • 如果當(dāng)前的可選值有值,調(diào)用成功;

多個調(diào)用可以連接在一起形成調(diào)用鏈媳维,當(dāng)其中任意一個節(jié)點返回nil,則整個調(diào)用鏈調(diào)用失敗返回nil遏暴。

2. 示例說明

class Person {
    var residence: Residence?
}

class Residence {
    var rooms = [Room]()
    var numberOfRooms: Int {
        return rooms.count
    }
    
    // 通過下標(biāo)訪問
    subscript(i: Int) -> Room {
        get {
            return rooms[i]
        }
        set {
            rooms[i] = newValue
        }
    }

    // 隱式返回類型Void. -> Void
    func printNumberOfRooms() {
        print("The number of rooms is \(numberOfRooms)")
    }
    var address: Address?
}

class Room {
    let name: String
    init(name: String) { self.name = name }
}

class Address {
    var buildingName: String?
    var buildingNumber: String?
    var street: String?
    
    func buildingIdentifier() -> String? {
        
        if let temp = buildingName {
            return temp
        }
        
        if let temp1 = buildingNumber, let temp2 = street {
            return temp1 + temp2
        }
        
        return nil
    }
}
  • 讀取屬性
let xiaoMing = Person()
if let roomCount = xiaoMing.residence?.numberOfRooms {
    print("xiaoMing's residence has \(roomCount) room(s).")
} else {
    print("Unable to retrieve the number of rooms.")
}
// Unable to retrieve the number of rooms.
  • 設(shè)置屬性
// 通過xiaoMing.residence來設(shè)定address屬性也會失敗侄刽,因為xiaoMing.residence當(dāng)前為nil
let address = Address()
address.buildingName = "2.5產(chǎn)業(yè)園"
address.buildingNumber = "88號"
address.street = "dongchang Road"
xiaoMing.residence?.address = address
  • 通過可選鏈?zhǔn)秸{(diào)用調(diào)用方法
if let _ = xiaoMing.residence?.printNumberOfRooms() {
    print("It was possible to print the number of rooms.")
} else {
    print("It was not possible to print the number of rooms.")
}
//It was not possible to print the number of rooms.
  • 通過可選鏈?zhǔn)秸{(diào)用訪問下標(biāo)
if let firstRoomName = xiaoMing.residence?[0].name {
    print("The first room name is \(firstRoomName).")
} else {
    print("Unable to retrieve the first room name.")
}
//Unable to retrieve the first room name.

六. 參考聲明

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市朋凉,隨后出現(xiàn)的幾起案子州丹,更是在濱河造成了極大的恐慌,老刑警劉巖杂彭,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件墓毒,死亡現(xiàn)場離奇詭異,居然都是意外死亡亲怠,警方通過查閱死者的電腦和手機所计,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來团秽,“玉大人主胧,你說我怎么就攤上這事∠扒冢” “怎么了踪栋?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長图毕。 經(jīng)常有香客問我夷都,道長,這世上最難降的妖魔是什么予颤? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任囤官,我火速辦了婚禮冬阳,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘治拿。我一直安慰自己摩泪,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布劫谅。 她就那樣靜靜地躺著见坑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪捏检。 梳的紋絲不亂的頭發(fā)上荞驴,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機與錄音贯城,去河邊找鬼熊楼。 笑死,一個胖子當(dāng)著我的面吹牛能犯,可吹牛的內(nèi)容都是我干的鲫骗。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼踩晶,長吁一口氣:“原來是場噩夢啊……” “哼执泰!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起渡蜻,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤术吝,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后茸苇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體排苍,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年学密,在試婚紗的時候發(fā)現(xiàn)自己被綠了淘衙。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡则果,死狀恐怖幔翰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情西壮,我是刑警寧澤遗增,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站款青,受9級特大地震影響做修,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一饰及、第九天 我趴在偏房一處隱蔽的房頂上張望蔗坯。 院中可真熱鬧,春花似錦燎含、人聲如沸宾濒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽绘梦。三九已至,卻和暖如春赴魁,著一層夾襖步出監(jiān)牢的瞬間卸奉,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工颖御, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留榄棵,地道東北人。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓潘拱,卻偏偏與公主長得像疹鳄,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子芦岂,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,592評論 2 353

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