想著要寫(xiě)個(gè)標(biāo)準(zhǔn)庫(kù)解析的系列文章菩鲜,卻有點(diǎn)不知道怎么入手惭蹂。
最開(kāi)始想的是按照官方文檔的順序?qū)懸槐榭瘢髞?lái)發(fā)現(xiàn)那樣寫(xiě)出來(lái)的跟我想要的文章不一樣萨醒。
然后想的是把Swfit標(biāo)準(zhǔn)庫(kù)的所有協(xié)議寫(xiě)一遍烧栋,附帶一些它的使用實(shí)例写妥。但是寫(xiě)了個(gè)Error
的協(xié)議,感覺(jué)有點(diǎn)空洞审姓,不好理解珍特。
最終決定按常用數(shù)據(jù)結(jié)構(gòu)遵循的協(xié)議,及各協(xié)議繼承的結(jié)構(gòu)來(lái)寫(xiě)魔吐。然后這就是第一篇文章了扎筒。一個(gè)Int
的實(shí)現(xiàn)遠(yuǎn)比我們想的要復(fù)雜的多。
標(biāo)準(zhǔn)庫(kù)中酬姆,
Int
類型遵循了6個(gè)協(xié)議嗜桌,接下來(lái)我們從簡(jiǎn)單到復(fù)雜逐一探討這些協(xié)議。
1. CustomPlaygroundQuickLookable
這個(gè)沒(méi)什么好說(shuō)的辞色,就是為了讓其支持Playground Quick Look
骨宠。
2. SignedInteger
提供有符號(hào)性。也就是同時(shí)支持正數(shù)和負(fù)數(shù)相满。
3. CVarArg
對(duì)應(yīng)C語(yǔ)言中的va_list
层亿,表示該類型可以作為可變參數(shù)。
我們使用者最常見(jiàn)到的樣子
int c_api (int n, ...)
對(duì)應(yīng)的方法定義為
int c_api (int, va_list arguments)
在Swift中的寫(xiě)法
func swiftAPI(_ x: Int, arguments: CVarArg...) -> Int {
return withVaList(arguments) { c_api(x, $0) }
}
可以看va_list
的源碼立美,其實(shí)就是一個(gè)通過(guò)固定偏移量棕所,來(lái)獲取所有參數(shù)的指針。
#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
對(duì)于任意類型悯辙,你都可以使用Mirror(reflect:)
方法來(lái)創(chuàng)建一個(gè)反射,但是如果你對(duì)系統(tǒng)默認(rèn)生成的反射對(duì)象不滿意迎吵,你可以遵循該協(xié)議然后自定義一個(gè)躲撰。
下面是Apple的說(shuō)法
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
在計(jì)算機(jī)的世界里击费,hash
值用于快速定位和查找集合中對(duì)象拢蛋。
一個(gè)類型為了存儲(chǔ)在集合中,該類型必須是可hash
的:該類型必須提供一種方法計(jì)算它的哈希值蔫巩,hash
為Int
類型谆棱,相等的對(duì)象hash
必須相同快压。
Swift
的所有基本類型(如String
,Int
垃瞧,Double
蔫劣,Bool
)默認(rèn)是可hash
的,可以作為集合的值或者字典的鍵个从。沒(méi)有關(guān)聯(lián)值的枚舉成員值默認(rèn)也是hash
的脉幢。
自身實(shí)現(xiàn)hashValue
是一個(gè)很復(fù)雜的過(guò)程,自定義對(duì)象實(shí)現(xiàn)hashValue
的時(shí)候可通過(guò)系統(tǒng)已經(jīng)實(shí)現(xiàn)的hashValue
嗦锐,使用適當(dāng)?shù)奈贿\(yùn)算來(lái)橋接實(shí)現(xiàn)嫌松。
Hashable
繼承自Equatable
,所以遵守Hashable
時(shí)奕污,需要實(shí)現(xiàn)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
協(xié)議和LosslessStringConvertible
協(xié)議的基礎(chǔ)上,添加了字節(jié)的改變碳默、位運(yùn)算贾陷、捕捉溢出或者是訪問(wèn)最大最小值等功能。也就是各種二進(jìn)制的操作腻窒。
遵循這個(gè)協(xié)議昵宇,一個(gè)整數(shù)類型,除了取反(負(fù)數(shù))的功能儿子,就全部有了瓦哎。
-
LosslessStringConvertible
繼承自:CustomStringConvertible
,可以用字符串表示的類型柔逼。
加了一個(gè)Lossless
關(guān)鍵字蒋譬,表示可以從字符串無(wú)損轉(zhuǎn)換過(guò)來(lái)的類型。 -
BinaryInteger
其父協(xié)議為以下四種:
1. CustomStringConvertible
通過(guò)字符串類型進(jìn)行初始化愉适。
2. Numeric
提供最基本的雙目運(yùn)算功能犯助,+
、-
维咸、*
剂买、+=
、-=
癌蓖、*=
瞬哼。
其父協(xié)議之一是Equatable
,也就是具備判斷相等的功能租副。
另一個(gè)父協(xié)議ExpressibleByIntegerLiteral
坐慰,表示可以直接通過(guò)Integer
(整數(shù))來(lái)進(jìn)行初始化。
也就是說(shuō)遵守這個(gè)協(xié)議用僧,就可以做最基本的整數(shù)類型初始化结胀,及加減乘除赞咙。
3. Hashable
支持hash
能力。
4. Strideable
連續(xù)的糟港,一維的攀操,可以被抵消和測(cè)量。
它的父協(xié)議是Comparable
着逐,Comparable
的父協(xié)議是Equatable
崔赌。
所以這個(gè)協(xié)議是表示:值是連續(xù)的,可以判斷是否相等耸别,可以判斷大小健芭,可以進(jìn)行+1
,-1
這種操作的值秀姐,可用...
表示范圍慈迈。也就是線性的值。
所以BinaryInteger
協(xié)議在支持上述四種協(xié)議功能的同時(shí)省有,提供的是數(shù)字類型之間的四種轉(zhuǎn)換功能痒留。
1. Range-Checked Conversion (檢查范圍的轉(zhuǎn)換)
方法:init(_:)
說(shuō)明:檢查邊界,小數(shù)轉(zhuǎn)整數(shù)直接省略小數(shù)蠢沿。超過(guò)范圍的會(huì)直接報(bào)運(yùn)行時(shí)錯(cuò)誤伸头。
let x: Int = 500
let z = Int8(x)
// Error: 過(guò)界
let e = Int8(127.75)
// e == 127
2. Exact Conversion (精確的轉(zhuǎn)換)
方法:init?(exactly:)
說(shuō)明:轉(zhuǎn)換結(jié)果為可空類型,超過(guò)邊界會(huì)返回nil
舷蟀,沒(méi)超過(guò)會(huì)返回Optional
類型的數(shù)值恤磷,小數(shù)轉(zhuǎn)整數(shù)時(shí),如果小數(shù)位不是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 (區(qū)域轉(zhuǎn)換)
方法:init(clamping:)
說(shuō)明:轉(zhuǎn)換的結(jié)果為目標(biāo)類型的最大值和最小值之間。大于最大值就返回最大值匈子,小于最小值就返回最小值河胎。
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(位模式轉(zhuǎn)換)
方法:init(truncatingIfNeeded:)
說(shuō)明:
正數(shù)之間的轉(zhuǎn)換,大的類型轉(zhuǎn)換成小的類型虎敦,會(huì)直接截掉(二進(jìn)制位上的截扔卧馈)多余的部分;小的類型轉(zhuǎn)換成大的類型其徙,會(huì)在前面添加0
占位吭历。
正數(shù)的擴(kuò)展,結(jié)果用0
占位擂橘。負(fù)數(shù)的擴(kuò)展,結(jié)果用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