Swift標準庫解析:Int

灰s發表於2017-12-22

想著要寫個標準庫解析的系列文章,卻有點不知道怎麼入手。

最開始想的是按照官方文件的順序寫一遍,後來發現那樣寫出來的跟我想要的文章不一樣。

然後想的是把Swfit標準庫的所有協議寫一遍,附帶一些它的使用例項。但是寫了個Error的協議,感覺有點空洞,不好理解。

最終決定按常用資料結構遵循的協議,及各協議繼承的結構來寫。然後這就是第一篇文章了。一個Int的實現遠比我們想的要複雜的多。

image.png
標準庫中,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.

反射的具體作用可以看這篇文章。

Swift 反射 API 及用法

5. Hashable

在計算機的世界裡,hash值用於快速定位和查詢集合中物件。

一個型別為了儲存在集合中,該型別必須是可hash的:該型別必須提供一種方法計算它的雜湊值,hashInt型別,相等的物件hash必須相同。

Swift的所有基本型別(如StringIntDoubleBool)預設是可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

image.png
其父協議為以下四種:

1. CustomStringConvertible

通過字串型別進行初始化。

2. Numeric

提供最基本的雙目運算功能,+-*+=-=*=。 其父協議之一是Equatable,也就是具備判斷相等的功能。 另一個父協議ExpressibleByIntegerLiteral,表示可以直接通過Integer(整數)來進行初始化。 也就是說遵守這個協議,就可以做最基本的整數型別初始化,及加減乘除。

3. Hashable

支援hash能力。

4. Strideable

連續的,一維的,可以被抵消和測量。 它的父協議是ComparableComparable的父協議是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
複製程式碼

相關文章