swift 編程實踐

swift 類型推斷

毫無疑問,類型推斷是現(xiàn)在編程語言的一個重要特性,它讓我們的代碼更加的簡潔易懂。但是swift的類型推斷有時候卻并非按照我們想像的工作。
比較下面兩段代碼:

    self.performSelector("selector")

    let selector = "selector"
    self.performSelector(selector)

乍看上去兩段代碼應該是等效的。然而事實卻是第一段代碼能夠順利編譯,第二段代碼會編譯失敗。究其原因,selector的類型會被推斷成為String,而performSelector不接受String類型的參數(shù)。第一段代碼的"selector"會被推斷成為Selector類型,因而順利編譯。
目前swift的類型推斷系統(tǒng)還不能完全根據(jù)上下文得出正確的類型,所以經(jīng)常使用option查看變量類型是一個推薦的做法

swift 類型推斷和編譯時間

考慮下面一段代碼:

    let myCompany = [
        "employees": [
            "employee 1": ["attribute": "value"],
            "employee 2": ["attribute": "value"],
            "employee 3": ["attribute": "value"],
            "employee 4": ["attribute": "value"],
            "employee 5": ["attribute": "value"],
            "employee 6": ["attribute": "value"],
            "employee 7": ["attribute": "value"],
            "employee 8": ["attribute": "value"],
            "employee 9": ["attribute": "value"],
            "employee 10": ["attribute": "value"],
            "employee 11": ["attribute": "value"],
            "employee 12": ["attribute": "value"],
            "employee 13": ["attribute": "value"],
            "employee 14": ["attribute": "value"],
            "employee 15": ["attribute": "value"],
            "employee 16": ["attribute": "value"],
            "employee 17": ["attribute": "value"],
            "employee 18": ["attribute": "value"],
            "employee 19": ["attribute": "value"],
            "employee 20": ["attribute": "value"],
        ]
    ]

這一段代碼非常簡單,但是如果這一段代碼需要編譯的話,將需要消耗數(shù)小時。
如果我們把上面的“String類型”,改成我們自定義的其他類型。編譯速度可能會有一些提升。但是當需要推斷的類型數(shù)量達到一定級別后。編譯速度依然是不可接受的。
推測其中的原因,應該是因為每添加一個元素,編譯器都需要確切的知道在當前步驟的確切類型。直接導致了編譯復雜度的直線上升。
如果我們直接聲明出常量myCompany的類型,編譯將在毫秒級別時間內完成。
當需要推斷的類型比較復雜的時候,直接聲明類型將顯著提升編譯效率

Optional

swift是一種靜態(tài)強類型語言。其中能體現(xiàn)強類型的特性之一就是optional變量。
在很多語言當中,nil或者null能夠賦值給任意的class類型變量。但是swift不行,如果我們想給任何變量賦值nil,我們一定需要將其設置為optional類型。如

    let optional: Int? = nil

究其本質,optional其實就是一個enum。其實現(xiàn)為

    enum Optional<T> {
        case Some(T)
        case None
    }

"?"其實是蘋果為Optional這種特殊的enum提供的語法糖。對于必須有值和可能有值兩種情況進行了非常明確的區(qū)分。

Enum & Optional

考慮下面一個場景,一個客戶需要訂火車票去其他城市,但是他有可能訂往返票,也有可能訂單程票,或者是訂聯(lián)程票。
我們要初始化這一段行程,我們可以用Optional和Enum分別進行設計。
首先,我們定義一個叫做OriginDestination的class,代表單邊行程。
設計一,使用Optional:

    class Itinerary {

        let departing: OriginDestination
        let returning: OriginDestination?
        let secondDeparting: OriginDestination?

        init(departing: OriginDestination, returning: OriginDestination?, secondDeparting: OriginDestination?) {
            self.departing = departing
            self.returning = returning
            self.secondDeparting = secondDeparting
        }
    }

我們需要通過判斷returning和secondDeparting來判斷這一段行程的類型。這種設計有幾點問題:

  • 無法限定外面?zhèn)鬟M來的值的可能性。比如當returning和secondDeparting同時有值的時候,我們并沒有為這種情況定義業(yè)務邏輯,也就無法為該種情況提供實現(xiàn)。但是從代碼的角度來看,并不能限定這種情況的發(fā)生。
  • 擴展性,當需要擴展票的類型的時候,這段代碼將變的難以擴展和維護。
  • 可讀性,在不那么了解業(yè)務邏輯的情況下,并不能看出returning和secondDeparting二選一的條件。或許只能通過文檔約定。

設計二,使用Enum:

    enum ItineraryType {
        case Oneway(departing: OriginDestination)
        case RoundTrip(departing: OriginDestination, returning: OriginDestination)
        case OpenJaw(departing: OriginDestination, secondDeparting: OriginDestination)
    }

    class Itinerary {

        let itinerary: ItineraryType

        init(itinerary: ItineraryType) {
            self.itinerary = itinerary
        }
    }

這一次設計意圖更加明顯,直接由ItineraryType來判斷行程的類型,并且將對應的值存入對應的case當中。用Optional設計的幾點問題就迎刃而解。

推薦使用帶值的Enum來實現(xiàn)這種類型多選一,并且有不同的值的組合的需求

Protocol Extension

面向接口編程是OO的設計準則之一,swift中的Protocol約束能力強,并且還能進行擴展。讓我們來看一下兩組實現(xiàn):
實現(xiàn)一:

    protocol P1 {
        func optionalMethod()
    }

    extension P1 {
        func optionalMethod() {
            print("default optional method")
        }
    }

    class C1: P1 {
        func optionalMethod() {
            print("override optional method")
        }
    }

    let c1: P1 = C1()
    c1.optionalMethod()

我們可以得到的輸出結果為:override optional method

實現(xiàn)二:

    protocol P2 {
    }

    extension P2 {
        func optionalMethod() {
            print("default optional method")
        }
    }

    class C2: P2 {
        func optionalMethod() {
            print("override optional method")
        }
    }

    let c2: P2 = C2()
    c2.optionalMethod()

我們可以得到的輸出結果為:default optional method

以上兩組定義實現(xiàn),區(qū)別在于P1中申明了OptionalMethod方法,而P2中沒有。而結果是C1成功重寫此方法,C2重寫失效。
再觀察protocol extension,我們容易發(fā)現(xiàn)。我們可以很輕松的實現(xiàn)多重繼承的關系。而多重繼承帶來的復雜度相信大家都能夠體會。
在足夠簡單的情況下使用Protocol Extension。不要讓一時的方便造成復雜度的上升

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容