想著要寫個標準庫解析的系列文章,卻有點不知道怎麼入手。
最開始想的是按照官方文件的順序寫一遍,後來發現那樣寫出來的跟我想要的文章不一樣。
然後想的是把Swfit標準庫的所有協議寫一遍,附帶一些它的使用例項。但是寫了個Error
的協議,感覺有點空洞,不好理解。
最終決定按常用資料結構遵循的協議,及各協議繼承的結構來寫。然後這就是第一篇文章了。一個Int
的實現遠比我們想的要複雜的多。
Int
型別遵循了6個協議,接下來我們從簡單到複雜逐一探討這些協議。
1. CustomPlaygroundQuickLookable
這個沒什麼好說的,就是為了讓其支援Playground Quick Look
。
2. SignedInteger
提供有符號性。也就是同時支援正數和負數。
3. CVarArg
對應C語言中的va_list
,表示該型別可以作為可變引數。
我們使用者最常見到的樣子
int c_api (int n, ...)
複製程式碼
對應的方法定義為
int c_api (int, va_list arguments)
複製程式碼
在Swift中的寫法
func swiftAPI(_ x: Int, arguments: CVarArg...) -> Int {
return withVaList(arguments) { c_api(x, $0) }
}
複製程式碼
可以看va_list
的原始碼,其實就是一個通過固定偏移量,來獲取所有引數的指標。
#ifdef _M_ALPHA
typedef struct {
char *a0; /* pointer to first homed integer argument */
int offset; /* byte offset of next parameter */
} va_list;
#else
typedef char * va_list;
#endif
複製程式碼
4. CustomReflectable
對於任意型別,你都可以使用Mirror(reflect:)
方法來建立一個反射,但是如果你對系統預設生成的反射物件不滿意,你可以遵循該協議然後自定義一個。
下面是Apple的說法
You can create a mirror for any type using the Mirror(reflect:) initializer, but if you are not satisfied with the mirror supplied for your type by default, you can make it conform to CustomReflectable and return a custom Mirror instance.
反射的具體作用可以看這篇文章。
5. Hashable
在計算機的世界裡,hash
值用於快速定位和查詢集合中物件。
一個型別為了儲存在集合中,該型別必須是可hash
的:該型別必須提供一種方法計算它的雜湊值,hash
為Int
型別,相等的物件hash
必須相同。
Swift
的所有基本型別(如String
,Int
,Double
,Bool
)預設是可hash
的,可以作為集合的值或者字典的鍵。沒有關聯值的列舉成員值預設也是hash
的。
自身實現hashValue
是一個很複雜的過程,自定義物件實現hashValue
的時候可通過系統已經實現的hashValue
,使用適當的位運算來橋接實現。
Hashable
繼承自Equatable
,所以遵守Hashable
時,需要實現Equatable
的==
方法。
例如:
class Person: Hashable {
var name = ""
var age = 0
var hashValue: Int {
return name.hashValue ^ age.hashValue
}
}
func == (m1:Person, m2:Person) -> Bool {
return m1.name == m2.name && m1.age == m2.age
}
複製程式碼
6. FixedWidthInteger
在BinaryInteger
協議和LosslessStringConvertible
協議的基礎上,新增了位元組的改變、位運算、捕捉溢位或者是訪問最大最小值等功能。也就是各種二進位制的操作。
遵循這個協議,一個整數型別,除了取反(負數)的功能,就全部有了。
-
LosslessStringConvertible
繼承自:CustomStringConvertible
,可以用字串表示的型別。 加了一個Lossless
關鍵字,表示可以從字串無損轉換過來的型別。 -
BinaryInteger
1. CustomStringConvertible
通過字串型別進行初始化。
2. Numeric
提供最基本的雙目運算功能,+
、-
、*
、+=
、-=
、*=
。
其父協議之一是Equatable
,也就是具備判斷相等的功能。
另一個父協議ExpressibleByIntegerLiteral
,表示可以直接通過Integer
(整數)來進行初始化。
也就是說遵守這個協議,就可以做最基本的整數型別初始化,及加減乘除。
3. Hashable
支援hash
能力。
4. Strideable
連續的,一維的,可以被抵消和測量。
它的父協議是Comparable
,Comparable
的父協議是Equatable
。
所以這個協議是表示:值是連續的,可以判斷是否相等,可以判斷大小,可以進行+1
,-1
這種操作的值,可用...
表示範圍。也就是線性的值。
所以BinaryInteger
協議在支援上述四種協議功能的同時,提供的是數字型別之間的四種轉換功能。
1. Range-Checked Conversion (檢查範圍的轉換)
方法:init(_:)
說明:檢查邊界,小數轉整數直接省略小數。超過範圍的會直接報執行時錯誤。
let x: Int = 500
let z = Int8(x)
// Error: 過界
let e = Int8(127.75)
// e == 127
複製程式碼
2. Exact Conversion (精確的轉換)
方法:init?(exactly:)
說明:轉換結果為可空型別,超過邊界會返回nil
,沒超過會返回Optional
型別的數值,小數轉整數時,如果小數位不是0
,則返回nil
。
let x = Int16(exactly: 500)
// x == Optional(500)
let y = Int8(exactly: 500)
// y == nil
let e = Int8(exactly: 23.0) // integral value, representable
// e == Optional(23)
let f = Int8(exactly: 23.75) // fractional value, representable
// f == nil
複製程式碼
3. Clamping Conversion (區域轉換)
方法:init(clamping:)
說明:轉換的結果為目標型別的最大值和最小值之間。大於最大值就返回最大值,小於最小值就返回最小值。
let x = Int16(clamping: 500)
// x == 500
let y = Int8(clamping: 500)
// y == 127
let z = UInt8(clamping: -500)
// z == 0
複製程式碼
4. Bit Pattern Conversion(位模式轉換)
方法:init(truncatingIfNeeded:)
說明:
正數之間的轉換,大的型別轉換成小的型別,會直接截掉(二進位制位上的擷取)多餘的部分;小的型別轉換成大的型別,會在前面新增0
佔位。
正數的擴充套件,結果用0
佔位。負數的擴充套件,結果用1
佔位。
let q: Int16 = 850
// q == 0b00000011_01010010
let r = Int8(truncatingIfNeeded: q) // truncate 'q' to fit in 8 bits
// r == 82
// == 0b01010010
let s = Int16(truncatingIfNeeded: r) // extend 'r' to fill 16 bits
// s == 82
// == 0b00000000_01010010
let t: Int8 = -100
// t == -100
// t's binary representation == 0b10011100
let u = UInt8(truncatingIfNeeded: t)
// u == 156
// u's binary representation == 0b10011100
let v = Int16(truncatingIfNeeded: t)
// v == -100
// v's binary representation == 0b11111111_10011100
let w = UInt16(truncatingIfNeeded: t)
// w == 65436
// w's binary representation == 0b11111111_10011100
複製程式碼