1. 程式人生 > >Swift泛型語法高階處理一例

Swift泛型語法高階處理一例

Swift參考某幾種語言,增加了泛型這一機制,讓人又愛又恨。

泛型增加了語言的表現力,減少了冗餘,這是好訊息;然而壞訊息是:對於複雜的實現來說,七繞八不繞,語法容易把人搞暈…以下就是一例。

這是從我實際的專案中摘出來的例子,做了簡化。有童鞋看到後面的程式碼可能會問,這麼簡單的問題,幹嘛要繞圈寫這麼複雜的實現???這不前面說了麼,因為實際的專案複雜!!!這裡只是簡化到講解本主題,所以若有詞不達意,也請包涵。

這裡有一個通用協議和另一個Main協議,Main協議遵守通用協議:

protocol CommonDelegate {
    associatedtype Item

    func invoke(with
item:Item) } protocol MainDelegate:CommonDelegate { func save(with item:Item) }

這裡比較奇怪的是Item型別,它是什麼呢?它是實際要操作的Model。

因為上述協議和遵守協議的類在Framework中,而實際的Model在App裡,所以有必要再寫一個Model協議:

protocol FooDelegate{
    var name:String {get set}
    var id:Int {get set}
    var desc:String {get}
}

記住,實際的Foo資料模型類在App裡,它遵守FooDelegate協議:

class Foo:FooDelegate{
    var name:String
    var id:Int

    var desc: String{
        return "\(name):\(id)"
    }

    init(name:String,id:Int) {
        self.name = name
        self.id = id
    }
}

下面輪到”過渡”類Main隆重出場了:

class Main{
    var delegate:MainDelegate!

    func breed(){
        let
newFoo = Foo(name: "hopy", id: 1) self.delegate.save(with: newFoo) } }

沒錯,它只有一個breed方法,而其中又呼叫了委託的save方法。這裡很有意思,實際上它本身不幹啥活,具體幹啥還得委託delegate說了算。

上面這樣寫對麼???錯!!!

回到MainDelegate協議看一下,它繼承於CommonDelegate,其中有一個關聯型別Item,在宣告委託變數delegate的時候必須確定Item的型別,但是這裡啥也沒有說明白…

所以我們需要將Main類修改成如下形式:

class Main<T> where T:MainDelegate,T.Item:FooDelegate{
    var delegate:T!

    func breed(){
        let newFoo = Foo(name: "hopy", id: 1)
        self.delegate.save(with: newFoo)  //TODO:注意這行
    }
}

注意我們在Main裡綁定了一個泛型T,確定了Item的型別為FooDelegate。這裡Item的型別為什麼不是Foo???因為前面說過了實際的Model在App裡由使用者定義,它可以是一個CoreData的託管物件或是其他什麼別的東東,Framework控制不了,也沒心思理這些,不管實際資料模型是啥,只要遵守FooDelegate就行了。

看到上面TODO那行了麼?別急,我們最後再來說它。

OK,現在只剩最後一個實際“幹活”的類了,就叫它Maker吧:

class Maker:MainDelegate {

    typealias Item = Foo

    var main:Main<Maker>!

    func didLoad(){

        main = Main<Maker>()
        main.delegate = self

        main.breed()
    }

    func invoke(with item: Item) {
        print("invoke item:\(item.desc) done!!!")
    }

    func save(with item: Item) {
        print("save item [\(item.desc)] done!!!")
    }
}

因為顯然Maker在App裡,所以我們把Item和Foo繫結;實際上這裡也不可以和FooDelegate繫結,因為不可以將非實體型別和關聯型別繫結。

Maker裡的main屬性很有意思,它將Main的泛型型別設為自己。

上面的程式碼都可在Xcode的playground裡愉快地玩耍,大家可以實際執行下試試。

如果到這裡你還沒有暈,那麼最後我們再來聊一聊,前面TODO那一行。

假設你按我說的嘗試執行一下,你會失望的:就在TODO那行報錯了:

這裡寫圖片描述

為毛呢?協議裡save方法的引數型別是Item,實際Item型別卻是FooDelegate,你可能會想做一下強轉:

let newFoo = Foo(name: "hopy", id: 1) as FooDelegate

很遺憾,錯誤依舊!我們不可以在Maker裡將Item設定為FooDelegate,原因前面說過了。那麼這裡該怎麼寫呢?

很簡單,你不是要抽象麼?我就給你抽象:

let newFoo = Foo(name: "hopy", id: 1) as! T.Item

現在執行OK啦:

這裡寫圖片描述

這就是Swift泛型的折騰,謝謝觀賞!