swift 可以定義模板函數,如:
func testFunc<T>(datas: [T]) -> T{
//處理
}
這里有個T,使用指代類型的,這個方法定義出來,可以用來處理任意的數組:
let names = [String]()
testFunc(names)
let names2 = [Int]()
testFunc(names)
傳入String
的數組,T就是String;傳入Int
的數組,T就是Int.這個方法就像是一個模板,有了它,可以復刻出許多個不同的版本。
問題1:我想要這個T是具有某個特定方法的
舉個常用的例子:
比如寫一個用來找最小值的方法,func min<T>(datas: [T]) -> T?
,我肯定是希望它能處理數字、字符串、日期,甚至是自定義類的對象,那就需要這個被排序的數組里的對象,必須得實現一個比較的函數, 這個我才能知道那個大那個小,才能排序。而且我只需要這個比較函數就可以對所有類通用了。
比如可以寫成:
func min<T>(datas: [T]) -> T?{
if datas.count == 0 {
return nil
}
var min: T = datas.first!
for data in datas {
if data.compareTo(min) < 0{
min = data
}
}
return min
}
里面用了compareTo方法。我要怎么確定這個T一定是有這個方法的呢?
祭出法寶:protocol
聯想:一些人困惑協議有什么用?跟block/閉包有什么區別?這里的功能就是閉包無法替代協議的,其實協議和委托是可以不一起行動的。
定義一個協議:
protocol Comparable : NSObjectProtocol{
/**
和其他對象比較
- parameter other: 其他對象
- returns: 0 相等 -1 小于 1 大于
*/
func compareTo(other: Self) -> Int
}
然后把方法定義修改為:
func min<T:Comparable>(datas: [T]) -> T?
T后面限定了類型,指定了這個T是遵循類Comparable這個協議的,那么也就具有了compareTo這個方法。在我理解里,協議的核心就在這:表明某個類具有特定的方法/能力
問題2:多個協議怎么辦?
假如我想處理的T類型是需要具有兩個不同的能力,舉個現實的例子:一個榨汁機,它接收的東西應該同時具有可被碾碎和出水兩個特性,這兩個特性是分開的,因為餅干不出水和椰子碾不碎。對應到代碼,可悲碾碎是一個protocol里的一個方法,會出水是另一個protocol的方法。
多個協議只需要寫成:
func min<T:protocol<testProtocol1,testProtocol2>>(datas: [T]) -> T?
把兩個協議用protocol關鍵字裝起來就好了。
問題3:如果我還想這個T是某個特定的類呢?
比如我想T是class1這個類的對象,同時遵循testProtocol1和testProtocol2,怎么寫?
祭出法寶:where關鍵字
方法寫成:
func findMinTemplateFunc<T : testCalss
where T :protocol<testProtocol1,testProtocol2>>(datas: [T]) -> T?
把協議的限定方法where里面去,where還有其他用法,我也沒太用過,就不說了。
這個需求看起來好像很難發生,但只需要想一個東西就有了:抽象類。swift/OC里沒強掉這個概念,但是這個東西是存在的,比如CoreData里面的NSManageredObject,你會直接使用這個類來構造對象嗎?肯定不會,肯定要建自己的數據實體,也就是NSManageredObject的子類來操作了。
當有了抽象類做父類的時候,你處理的都是子類,如果你寫一個針對子類的模板方法,有些子類實現了testProtocol1,有些實現了testProtocol2,有些沒有。這時,就必須類、協議同時限定才能達到效果。
最后貼段例子:
加入找出數組里最小值,每個值根據value1 \ value2 和rate做一段算法后的值來比較:
protocol testProtocol1: NSObjectProtocol {
func value1() -> Int;
}
protocol testProtocol2: NSObjectProtocol {
func value2() -> Int;
}
//類似虛類的東西,比如NSManagedObject這種類,是不可能直接使用它來構建對象的,肯定是要配合自己建的CoreData實體
class testCalss: NSObject {
var rate: Int? = 1
}
func findMinTemplateFunc<T : testCalss
where T :protocol<testProtocol1,testProtocol2>>(datas: [T]) -> T?{
if datas.count == 0 {
return nil
}
var min : T = datas.first!
var minRealValue = min.value1() * 10 + min.value2() * 100
if let rate = min.rate {
minRealValue *= minRealValue * rate
}
for data in datas {
var realValue = data.value1() * 10 + data.value2() * 100
if let rate = data.rate {
realValue *= realValue * rate
}
if realValue < minRealValue {
min = data
minRealValue = realValue
}
}
return min
}
//例子
class subClass1: testCalss, testProtocol1, testProtocol2 {
func value1() -> Int {
return Int(arc4random() % 10)
}
func value2() -> Int {
return Int(arc4random() % 20)
}
}
class subClass2: testCalss, testProtocol1, testProtocol2 {
func value1() -> Int {
return Int(arc4random() % 100)
}
func value2() -> Int {
return Int(arc4random() % 200)
}
}
func runTest(){
var array1 = [subClass1]()
for _ in 0...99 {
array1.append(subClass1())
}
findMinTemplateFunc(array1)
var array2 = [subClass2]()
for _ in 0...99 {
array2.append(subClass2())
}
findMinTemplateFunc(array1)
}
runTest()