死磕The Swift Programming Language——學

karspb發表於2021-09-09

圖片描述

引言

The Swift Programming Language已經泛泛地看了幾遍了,由於看的不上心,花時間研究Swift的時候寥寥無幾,再加上平時用Swift寫的程式碼很少,一直還是Swift菜鳥一枚,現在打算和Swift死磕,先從The Swift Programming Language開始。

注:文中引用部分無特殊說明都是The Swift Programming Language裡的原文

基礎部分

Swift基於cocoa、cocoa touch,相容OC,能夠邊寫程式碼邊執行(Playground),它是一種語法靈活,編譯嚴格的一門語言。


32位平臺上Int與Int32位數相同,64位平臺上Int與Int64位數相同


Double是64位的,它至少有16位數字,後面五舍六入,如果是0.xxx形式的小數會顯示17位

Float是32位的,只顯示7位數字,後面進五舍六入,如果是0.xxx形式的小數會會顯示8位

這裡與programming Language中說明的不符,我是在playground裡測試的

下面是原文表述

注意:
Double精確度很高,至少有15位數字,而Float只有6位數字。選擇哪個型別取決於你的程式碼需要處理的值的範圍。


推斷浮點數的型別時,Swift總是會選擇Double,而不是Float,表示式中整型、浮點同時出現會被推斷為Double


不同進位制的整數數值字面量的表示:
0b二進位制數、0o八進位制數、0x十六進位制數


1.25e2 = 1.25 x 10^2 = 125.0
1.25e-2 = 1.25 x 10^-2 = 0.0125
0xFp2 = 15 x 2 ^ 2 = 60
0xFp-2 = 15 x 2^ -2 = 3.75


數值字面量可以新增格外格式增加可讀性:


let num1 = 000123.456
let num2 = 1_000_000
let num3 = 1_000_000.000_000_1

---

整數轉換要向精度大的一方轉換

>```let twoThousand: UInt16 = 2_000let one: UInt8 = 1let twoThousandAndOne = twoThousand + UInt16(one)

這裡注意:UInt16(one)不同於OC中的強轉,而是呼叫了UInt16類的建構函式,該類的建構函式可以接受一個UInt8型別的物件罷了


整型和浮點進行運算,統一先轉成浮點,字面量本身沒有型別之分,3和1.5是可以直接相加的,但是:
let num1 = 3
let num2 = 1.5
num1和num2就不能直接相加,num1和num2是有型別之分的

圖片描述


給型別取別名

typealias AudioSample = UInt16


Bool 型別:
值是true或false,在需要Bool值的時候如果傳入非Bool值,swfit會報錯,例如if判斷的時候必須是Bool值,這個與OC不同。


元組(tuples)形式和用法都很簡單,基本看一下就知道怎麼用,元組作為函式的返回值很有用


let http404Error = (404, "Not Found")
let (statusCode, statusMessage) = http404Error
print("The status code is (statusCode)")
let (justTheStatusCode, _) = http404Error
print("The status code is (http404Error.0)")
let http200Status = (statusCode: 200, description: "OK")
print("The status code is (http200Status.statusCode)")

---###可選:OptionalSwift不允許你宣告一個變數,而不對其初始化,這一點OC就很寬泛,但是有時候嚴格的編譯會產生一些問題:
例如,我們都知道VC有個view屬性,但是也都知道view屬性的賦值並不是在VC初始化的時候進行的,而是在loadView函式中進行的,這在Swift中就是個問題,實際上lazy load或者在宣告之後再初始化的場景還是很多的。

Swift如何解決這個問題?答案:可選(Optional,用在型別後面加?表示)

public enum Optional : _Reflectable, NilLiteralConvertible {
case None
case Some(Wrapped)
//系統原始碼,忽略下面的程式碼
}

Optional可選有兩個值:None或Some(Wrapped)。

OC中的nil表示缺少一個合法物件,僅用於修飾oc物件,非oc物件用NSNotFound表示,這是Swift與OC很不同的地方,這也體現了swift是編譯嚴格的語言。

let a : Int = 1    //a是一個Int物件(swift這裡的設計參照了java一切皆物件的思想)
let b : Int? = 1   //b是一個Optional物件,物件值是Some(Wrapped)因為此時b是賦值了的,而這裡的Wrapped指的就是Int
let c : Int? = nil //c是一個Optional物件,物件值是None

![](http://upload-images.jianshu.io/upload_images/1490498-cf6c43f22d4274bd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)c是Optional型別的物件,其實是一個enum,是可以用nil初始化的,其實就是enum中的None,d是一個Int物件是不能用nil初始化的。###強制拆包!:透過上面的瞭解我們知道:一個用?修飾的物件再也不是你想象中的那個物件了,**所有用?修飾的型別所產生的物件都是Optional物件**,這對理解?和!很重要的。
例如:上面例子中的b是一個Int?型別,編譯器把他當成是一個Optional物件來處理,而不是Int物件來處理。

![注意看右邊的輸出](http://upload-images.jianshu.io/upload_images/1490498-fad950063a256c9e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)問題來了:
我用b是要把它當一個Int物件來用,它現在是一個Optional物件怎麼用?
答案是:用"!"進行強制拆包(forced unwrapping).就可以得到裡面的值,要麼是None,要麼是Some(Wrapped),在這裡,Some(Wrapped)就是Int。

![注意看右邊的輸出和上面的圖對比來理解?和!](http://upload-images.jianshu.io/upload_images/1490498-ba7e3e8576a31ed9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)問題又來了:
!有什麼用?
答案,!十分有用,一旦你用?宣告宣告瞭一個變數,它就不再是你期望的那個型別了,它是Optional型別了(反覆強調了N遍,別嫌煩啊),所以它就**不能和其他型別愉快的玩耍了**。

![](http://upload-images.jianshu.io/upload_images/1490498-dd08610731e947ac.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)a和b僅僅是在宣告的時候差了一個?就不能在一起玩耍了,這個時候就要用!強制拆包來解決問題了,我們對可選型別進行拆包後的物件就是我們期望的物件,就可以和其它相同物件一起玩耍了。

![](http://upload-images.jianshu.io/upload_images/1490498-f3dc950e7af978de.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)###可選繫結(optional binding):>使用*可選繫結(optional binding)*來判斷可選型別是否包含值,如果包含就把值賦給一個臨時常量或者變數。可選繫結可以用在if和while語句中,這條語句不僅可以用來判斷可選型別中是否有值,同時可以將可選型別中的值賦給一個常量或者變數。

if let constantName = someOptional {
statements
}

我們可以透過一個很簡單的例子把可選繫結理解的更透徹一點:

![](http://upload-images.jianshu.io/upload_images/1490498-57272568fe5adfa4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

透過例子和programming Language中的說明我們可以做如下推斷:
1.我們用 if let tempX = x這種表達的時候,x一定是一個可選型別,否則報錯(tempA)
2.if let tempX = x 這種表達形式可以理解成:**判斷可選x是否包含值,如果包含值,賦值成功,如果不包含值,賦值失敗,賦值成功、失敗作為if判斷的Bool條件,也就是說if判斷的是let tempX = x這個整體,與tempX賦值後是什麼型別無關**,這裡需要理解一下,還有一點是tempX = x賦值成功後其實並不是單純的賦值,**x是先拆包後賦值給tempX的**,這對於理解可選繫結很重要(tempB,及其輸出資訊)。
3.可選繫結的變數或常量只能在if中使用,else中是不能用的(tempC)。

有了上面例子對可選繫結的理解,再理解programming Language中出現的這段程式碼就比較簡單了:

>```if let actualNumber = Int(possibleNumber) {    
    print("'(possibleNumber)' has an integer value of (actualNumber)")
} else {    
    print("'(possibleNumber)' could not be converted to an integer")
}

Int(possibleNumber)這種情況上面已經介紹過,就是呼叫Int類的建構函式,這個建構函式返回的是一個Int?物件,因為用possibleNumber並不一定能保證初始化Int成功,如果possibleNumber="haha"初始化就會失敗,所以返回的是Int?而不是Int,理解了這一點,再加上上面可選繫結的例子,這個程式碼就一清二楚了。

你可以包含多個可選繫結在if語句中,並使用where子句做布林值判斷。

if let firstNumber = Int("4"), secondNumber = Int("42") where firstNumber 

隱式解析可選型別:

let a :Int? = 1
我們之前介紹的可選有一個問題,我們實際需要的是Int,但是Int?給我的是一個可選Optional物件,裡面包了一個Some(Wrapped)是Int,這使得我們每次用a的時候都要對Optional物件拆包才能用,很是麻煩。
如何解決這個問題?
答案:隱式解析可選型別
隱式解析可選型別:在宣告常量或變數的時候用"!"代替"?"就是宣告瞭一個隱式解析可選型別,有點暈?看個例子就清楚了。

圖片描述

我們在用普通可選的時候(就是用?修飾的),每次使用都要拆包,才能用,而隱式解析可選型別不需要,直接就可以用,是不是方便了很多,怎麼理解隱式解析可選型別?

有時候在程式架構中,第一次被賦值之後,可以確定一個可選型別總會有值。在這種情況下,每次都要判斷和解析可選值是非常低效的,因為可以確定它總會有值。

這是隱式解析可選型別存在的意義。

一個隱式解析可選型別其實就是一個普通的可選型別,但是可以被當做非可選型別來使用,並不需要每次都使用解析來獲取可選值。

這是隱式解析可選型別的作用

我們可以理解隱式解析可選型別是對普通可選進行自動拆包,省去了我們手動拆包的麻煩,但是如果一個隱式解析可選型別如果沒有值的時候,你去嘗試取它的值就會發生執行時錯誤,就像一個普通可選型別強制拆包(!)但是它卻沒有值的時候也會發生執行時錯誤。普通可選和隱式解析可選側重點不同,前者是安全,後者是方便,如何取捨由自己決定。

我們同樣可以在if和while裡讓隱式解析可選型別和nil進行比較(只有可選型別才能和nil進行比較,因為普通型別的值是不可能是nil的),也可以對其進行可選繫結。

還記得我們引入可選Optional的時候引入的VC的view屬性的例子嗎,它就是一個隱式解析可選型別

//系統原始碼publicvar view: UIView! // The getter first invokes [self loadView] if the view hasn't been set yet. Subclasses must call super if they override the setter or getter.

個人理解:可選、隱式解析可選是swift嚴編譯的一個很好的提現,就這一點來說swift對於提高我們程式設計的嚴密性還是有一定幫助的。


Swift中斷言和OC差不多

assert(age > 0, "A person's age cannot be less than zero")

斷言資訊可省略

assert(age > 0)

release環境或者基於release複製出來的新的編譯環境下,斷言是失效的

總結

作為Swift菜鳥,在死磕Swift的道路上希望多與大家溝通討論,多向大家學習。

歡迎大家和我交流溝通,文章中有任何錯誤和漏洞,懇請指正,謝謝。




來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2001/viewspace-2811253/,如需轉載,請註明出處,否則將追究法律責任。

相關文章