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。不要讓一時的方便造成復雜度的上升