泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型
使用泛型代码,可以写出可重用的函数和数据结构,只要它们满足你所定义的约束,它们就能够适用于各种类型
Swift 提供了泛型让你写出灵活且可重用的函数和类型
Swift 标准库是通过泛型代码构建出来的
-
Swift 的数组和字典类型都是泛型集
你可以创建一个Int数组,也可创建一个String数组,或者甚至于可以是任何其他 Swift 的类型数据数组
泛型实例:
实例1:系统函数实例
标准库中的
min
函数,求两个或者多个相同类型元素的最小值
func min<T: Comparable>(_ x: T, _ y: T) -> T {
return y < x ? y : x
}
min
的两个参数和返回值泛型的唯一约束是它们三者都必须是同样的类型 T
,而这个 T
需要满 足 Comparable
。只要满足这个要求,T
可以是任意类型,它可以是 Int
,Float
,String
或者甚至是在编译时未知的定义在其他模块的某个类型
min
函数如果不使用泛型,具体实现:
// 比较两个 Int 类型的值,返回最小值
func min(_ x: Int, _ y: Int) -> Int {
return y < x ? y : x
}
// 比较两个 Double 类型的值,返回最小值
func min(_ x: Double, _ y: Double) -> Double {
return y < x ? y : x
}
// 比较两个 Character 字符类型的值,返回ASCii最小值字符
func min(_ x: Character, _ y: Character) -> Character {
return y < x ? y : x
}
// ......等所有符合 Comparable 可比较协议的类型
实例2:自定义泛型函数
交换两个相同类型元素的值
// 交换两个 Int 变量的函数
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let tempA = a
a = b
b = tempA
} // 交换两个 Double 变量的函数
func swapTwoDoubles(_ a: inout Double, _ b: inout Double) {
let tempA = a
a = b
b = tempA
} // 交换两个 String 变量的函数
func swapTwoStrings(_ a: inout String, _ b: inout String) {
let tempA = a
a = b
b = tempA
}
可以看出,这几个方法,代码冗余。使用泛型,很好的解决了这个问题
// 使用泛型 定义一个交换两个变量的函数
func swapTwoValues<T: Comparable>(_ a: inout T, _ b: inout T) {
let tempA = a
a = b
b = tempA
}
这里的 变量 a
和 b
可以是任意符合 Comparable
协议的类型(T
)的变量,其中T
包括:Int, Double, String
…
所以,使用泛型,简化了代码,同时也能简化负责的逻辑
类型约束
参数类型约束
类型约束指定了一个必须继承自指定类的类型参数,或者遵循一个特定的协议或协议构成
代码样式示例:
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// 这里是泛型函数的函数体部分
}
第一个类型参数 T
,有一个要求 T
必须是 SomeClass
子类的类型约束;
第二个类型参数 U
,有一个要求 U 必须符合 SomeProtocol
协议的类型约束。
Where 语句
类型约束能够确保类型符合泛型函数或类的定义约束
可以在参数列表中通过
where
语句定义参数的约束
可以写一个where
语句,紧跟在在类型参数列表后面,where
语句后跟一个或者多个针对关联类型的约束,以及(或)一个或多个类型和关联类型间的等价(equality
)关系。
代码示例:判断是否是栈中的顶部元素(最后一个加入的元素)
extension Stack where Element: Equatable {
func isTop(_ item: Element) -> Bool {
guard let topItem = items.last else {
return false
}
return topItem == item
}
}
泛型在实际开发中的运用举例
1、使用泛型快速获取 ReusableCell
extension UITableView {
/// 获取 UITableViewCell
func dequeueReusableCell<T: UITableViewCell>(_ identifier: String) -> T {
let cell = self.dequeueReusableCell(withIdentifier: identifier) as! T
return cell
}
}
extension UICollectionView {
func dequeueReusableCell<T: UICollectionViewCell>(_ identifier: String, indexPath: IndexPath) -> T {
let cell = self.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath) as! T
return cell
}
}
数组分组,将一个数组分为二维数组
extension Sequence {
func group<U: Hashable>(by key: (Iterator.Element) -> U) -> [U:[Iterator.Element]] {
var categories: [U: [Iterator.Element]] = [:]
for element in self {
let key = key(element)
if case nil = categories[key]?.append(element) {
categories[key] = [element]
}
}
return categories
}
}
字典的分类,通过传入 KEY 获取自动匹配字典返回数据值的类型
extension Dictionary where Value: Any {
func decoding<T>(with key: Key) -> T? {
guard let any: Any = self[key] else {
return nil
}
if let value: T = any as? T {
return value
} else {
switch T.self {
case is String.Type:
switch any {
case let someInt as Int:
return String(someInt) as? T
case let someDouble as Double:
return String(someDouble) as? T
case let someBool as Bool:
return String(someBool) as? T
default:
return nil
}
case is Int.Type:
if let someString: String = any as? String {
return Int(someString) as? T
} else if let someDouble: Double = any as? Double {
return Int(someDouble) as? T
} else {
return nil
}
case is Double.Type:
if let someString: String = any as? String {
return Double(someString) as? T
} else if let someInt: Int = any as? Int {
return Double(someInt) as? T
} else {
return nil
}
case is Bool.Type:
if let someString: String = any as? String {
return Bool(someString) as? T
} else {
return nil
}
default:
return nil
}
}
}
}
判断两个实例类型是否相同
func sameType<T,E>(one: T, inout two: E) -> Bool {
if(one.dynamicType == two.dynamicType) {
return true
} else {
return false
}
}
获取数组中某个元素的索引
func find <T: Equatable> (array: [T], item : T) ->Int? {
var index = 0
while(index < array.count) {
if(item == array[index]) {
return index
}
index++
}
return nil
}