前言
OC缺乏一個(gè)重要特性,不支持泛型峭拘。Swift擁有了這一特性俊庇,是靈活性的語法狮暑,在函數(shù)、結(jié)構(gòu)體辉饱、類搬男、枚舉中都可以應(yīng)用,相當(dāng)于暫位符的作用彭沼,當(dāng)類型暫時(shí)不確定缔逛,只有等到調(diào)用函數(shù)時(shí)才能確定具體類型的時(shí)候可以引入泛型。
簡(jiǎn)單理解泛型就是先占位姓惑,具體占位做什么褐奴,用的時(shí)候再說。Swift的Array和Dictionary類型都是泛型集
應(yīng)用場(chǎng)景
簡(jiǎn)單模仿一個(gè)棧操作于毙,主要實(shí)現(xiàn)出棧敦冬、入棧兩個(gè)功能
// MARK: - 模仿棧
class TestStack {
var valueArray: [Int] = []
/// 壓棧
func push(value: Int) -> () {
valueArray.append(value)
}
/// 出棧
func pop() -> (Int?) {
if let lastValue = valueArray.last {
valueArray.removeLast()
return lastValue
}else {
return nil
}
}
}
這樣實(shí)現(xiàn)的棧只能操作Int類型,如果需求變更唯沮,要處理String類型匪补,怎么解決?替換所有Int為String可以解決烂翰,但這種代碼明顯上不了臺(tái)面。此外另外我們還會(huì)想到Any類型蚤氏,如下
// MARK: - 模仿棧
class TestStack {
var valueArray: [Any] = []
/// 壓棧
func push(value: Any) -> () {
valueArray.append(value)
}
/// 出棧
func pop() -> (Any?) {
if let lastValue = valueArray.last {
valueArray.removeLast()
return lastValue
}else {
return nil
}
}
}
如此TestStack可操作類型就不局限于一種了甘耿,但是帶來了兩個(gè)問題:1、數(shù)據(jù)類型安全問題竿滨;2佳恬、每次對(duì)棧進(jìn)行操作時(shí),都需要進(jìn)行一系列繁瑣的類型轉(zhuǎn)換(casting操作于游,使用as來進(jìn)行類型轉(zhuǎn)換)
這里簡(jiǎn)單介紹下Any和AnyObject的區(qū)別:
在 Swift 3 之前毁葱,我們可以寫完一個(gè)項(xiàng)目都只用 AnyObject 來代表大多數(shù)實(shí)例,好像不用與 Any 類型打交道贰剥。但事實(shí)上倾剿,Any 和 AnyObject 是有明顯區(qū)別的,因?yàn)?Any 可以代表 struct蚌成、class前痘、func 等等幾乎所有類型,而 AnyObject 只能代表 class 生成的實(shí)例担忧。
那為什么之前我們?cè)?Swift 2 里可以用 [AnyObject] 聲明數(shù)組芹缔,并且在里面放 Int、String 等 struct 類型呢瓶盛?這是因?yàn)?Swift 2 中最欠,會(huì)針對(duì)這些 Int示罗、String 等 struct 進(jìn)行一個(gè) Implicit Bridging Conversions,在 Array 里插入他們時(shí)芝硬,編譯器會(huì)自動(dòng)將其 bridge 到 Objective-C 的 NSNumber蚜点、NSString 等類型,這就是為什么我們聲明的 [AnyObject] 里可以放 struct 的原因吵取。
但在 Swift 3 當(dāng)中禽额,為了達(dá)成一個(gè)門真正的跨平臺(tái)語言,相關(guān)提案將 Implicit Bridging Conversions 給去掉了皮官。所以如果你要把 String 這個(gè) struct 放進(jìn)一個(gè) [AnyObject] 里脯倒,一定要 as NSString,這些轉(zhuǎn)換都需要顯示的進(jìn)行了——畢竟 Linux 平臺(tái)默認(rèn)沒有 Objective-C runtime捺氢。這樣各平臺(tái)的表現(xiàn)更加一致藻丢。當(dāng)然這是其中一個(gè)目標(biāo)。
參照泛型的特性摄乒,定義一個(gè)泛型類型悠反。使用泛型后的示例代碼如下:
`
// MARK: - 模仿棧
class TestStack<T> {
var valueArray: [T] = []
/// 壓棧
func push(value: T) -> () {
valueArray.append(value)
}
/// 出棧
func pop() -> (T?) {
if let lastValue = valueArray.last {
valueArray.removeLast()
return lastValue
}else {
return nil
}
}
}
`
使用泛型:在初始化時(shí)通過明確的類型(這里用Int示例)來定義參數(shù),之后編譯器會(huì)將所有的泛型(T)替換成Int類型(如此實(shí)現(xiàn)的棧馍佑,最大優(yōu)勢(shì)在于能夠匹配任何類型)斋否。
// 示例1:Int
let myStack1 = TestStack<Int>()
myStack1.push(value: 1)
myStack1.push(value: 2)
myStack1.push(value: 3)
print(myStack1.pop() ?? "0")
// 示例2:String
let myStack2 = TestStack<String>()
myStack2.push(value: "a")
myStack2.push(value: "b")
myStack2.push(value: "c")
print(myStack2.pop() ?? "0")
泛型約束
泛型約束大致分為以下幾種:
協(xié)議約束,泛型類型必須遵循某些協(xié)議
繼承約束拭荤,泛型類型必須是某個(gè)類的子類類型
條件約束茵臭,泛型類型必須滿足某種條件
協(xié)議約束
還拿TestStack棧說事兒,這里添加一個(gè)函數(shù)isContainValue舅世,要判斷棧中是否包含傳入的元素旦委,需要用到等式符(==)和不等符(!=)對(duì)任何兩個(gè)該類型進(jìn)行比較。Swift標(biāo)準(zhǔn)庫(kù)中定義了一個(gè)Equatable協(xié)議雏亚,只有支持了這個(gè)協(xié)議才可以使用等式符(==)和不等符(!=)缨硝,否則報(bào)紅。所有的Swift標(biāo)準(zhǔn)類型自動(dòng)支持Equatable協(xié)議罢低,但是泛型由于不確定類型查辩,系統(tǒng)不知對(duì)象是否支持Equatable協(xié)議,所以直接使用==或!=報(bào)紅
解決方案网持,讓TestStack棧中宜肉,讓泛型T遵循Equatable協(xié)議
繼承約束
這里定義了三個(gè)類Person、Leader翎碑、Coder谬返,其中Leader是繼承Person,
// MARK: - 人
class Person: NSObject {
func watchTV() -> () {
print("Person看電視:人民的名義")
}
}
// MARK: - 領(lǐng)導(dǎo)
class Leader: Person {
override func watchTV() -> () {
print("Leader看電視:人民的名義")
}
}
// MARK: - 程序員
class Coder {
func watchTV() -> () {
print("coder看電視:人民的名義")
}
}
func testWatchTV<T: Person>(obj:T) -> () {
obj.watchTV()
}
testWatchTV函數(shù),接受一個(gè)泛型參數(shù)日杈,要求該泛型類型必須繼承Person遣铝,否則爆紅佑刷。
條件約束
在類型名后面使用where來指定對(duì)類型的特殊需求,比如限定類型實(shí)現(xiàn)某一個(gè)協(xié)議酿炸,限定兩個(gè)類型是相同的瘫絮,或者限定某個(gè)類必須有一個(gè)特定的父類等
// 定義一個(gè)協(xié)議
@objc protocol TestProtocol {
@objc optional func testOne() -> ()
}
// MARK: - 人
class Person: NSObject, TestProtocol {
var name: String?
func watchTV() -> () {
print("Person看電視:人民的名義")
}
}
// MARK: - 領(lǐng)導(dǎo)
class Leader: NSObject {
var name: String?
func watchTV() -> () {
print("Leader看電視:人民的名義")
}
}
testCondition函數(shù)要求傳入的參數(shù)都是P類型的繼承自Person,L類型繼承自Leader填硕,同時(shí)還附加了條件(where)這兩個(gè)類都遵守了TestProtocol協(xié)議麦萤,由于Leader類型沒有遵守TestProtocol協(xié)議,條件不滿足所以爆紅扁眯。修改方法則是Leader類遵守TestProtocol壮莹。
參考鏈接:http://www.reibang.com/p/a907f0c09a60
參考鏈接:http://swift.gg/2015/09/16/swift-generics/