和大多先进编程语言一样,swift拥有不少可以归类 泛型编程 的特性.
重载
拥有同样的名字,但是参数和返回类型不同的多个方法互相被称为重载方法,方法的重载不能称为泛型。不过和泛型类似我们可以将多种类型使用在同一接口。
- 自由函数的重载 genericity.playground
// 重载
func raise(_ base: Double, exponent: Double) -> Double {
return pow(base, exponent)
}
func raise(_ base: Float, exponent: Float) -> Float {
return pow(base, exponent)
}
let double = raise(2.0, exponent: 3.0)
type(of: double)
let float: Float = raise(2.0, exponent: 3.0)
type(of: float)
// swift 有一套负载的规则来确定到底使用哪个重载函数,根据是否泛型和传入参数的类型来确定优先级。
// 不过总的来说它会选择最具体的那一个,具体来说非通用函数的优先级高于通用函数
func log(_ view: View) {
print("It's a \(type(of: view)), frame: \(view.frame)")
}
// 给label写个重载
func log(_ view: UILabel) {
let text = view.text ?? "empty"
print("It's a label, text: \(text)")
}
// 这是 传入label 会调用专门的函数 而其他类型则调用泛型
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
label.text = "Password"
log(label)
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
log(button)
// 需要注意重载的使用是在编译期间静态决定的,也就是说编译器会根据类型决定调用哪一个函数
// 重载不会考虑运行时的动态类型 如果需要运行时函数 那么应该写定义到类型上的方法 而不是自由函数
let views = [label, button]
for view in views {
log(view)
}
- 运算符的重载
使用操作符重载时,编译器会有一些奇怪的行为,既泛型的版本本来是更好的选择时,类型检查器还是会选择那些非泛型的重载。
// 定义一个幂运算**
precedencegroup ExponentiationPrecedence {
associativity: left // 左结合
higherThan: MultiplicationPrecedence // 运算等级高于乘法*
}
infix operator **: ExponentiationPrecedence
func **(lhs: Double, rhs: Double) -> Double {
return pow(lhs, rhs)
}
func **(lhs: Float, rhs: Float) -> Float {
return pow(lhs, rhs)
}
2.0**3.0
/// 上面的代码 和上一节的raise是等价的
// 一个整数的重载
func **(lhs: I, rhs: I) -> I {
let result = pow(Double(lhs), Double(rhs))
return numericCast(Int(result))
}
// 看起来能运行 实际不行 因为类型检查器会使用非 泛型版本的运算符 但是又不能确定是哪一种 float 还是 double
//2 ** 3
// 所有要显示的指出参数或返回值类型
let result: Int = 2 ** 3
- 使用泛型约束重载
判定一个数组的元素是否包含在另一个数组中,既是不是另一个子集。
标准库中有一个方法 isSubset(of:)的方法不过这个方法只适用于Set这种满足SetAlgebra协议的类型 我们写一个适应更多范围的方法
// 遵守Equatable元素组成的序列
extension Sequence where Iterator.Element: Equatable {
/// 如果self 中所有元素包含在other中则返回true
func isSubset(of other: [Iterator.Element]) -> Bool {
for element in self {
guard other.contains(element) else {
return false
}
}
return true
}
}
let oneToThree = [1,2,3]
let fiveToOne = [5,4,3,2,1]
oneToThree.isSubset(of: fiveToOne)
// 这个算法有个重大缺陷 就是它的时间复杂度是O(m*n) 因为contains是O(n)
// 通过收紧元素类型 写出更好的版本。比如要求元素 满足Hashable 复杂度就是O(m+n)
// 如果两个数组都是 1000 个元素 这就是 2000 和 100万次的区别
extension Sequence where Iterator.Element: Hashable {
func isSubset(of other: [Iterator.Element]) -> Bool {
let otherSet = Set(other)
for element in self {
guard otherSet.contains(element) else {
return false
}
}
return true
}
}
对集合采用泛型操作
一个swift 实现的二分查找
//extension Array {
// func binarySearch(for value: Element, areInIncreasingOrder: (Element, Element) -> Bool) -> Int? {
// var left = 0
// var right = count - 1
// while left <= right {
// let mid = (left + right) / 2
// let candidate = self[mid]
// if areInIncreasingOrder(candidate, value) {
// left = mid + 1
// } else if areInIncreasingOrder(value, candidate) {
// right = mid - 1
// } else {
// return mid
// }
// }
//
// return nil
// }
//}
//extension Array where Element: Comparable {
// func binarySearch(for value: Element) -> Int? {
// return self.binarySearch(for: value, areInIncreasingOrder: < )
// }
//}
/// 按照swift 标准库的一般规范 返回一个 nil 表示未找到
/// 它被定义两次 其中一次由用户提供比表参数,另一次依赖某个协议 是简单版本
/// 它不支持 ArraySlice 和 ContigousArray 它们没有 一个 Int 的 索引
/// 使用 泛型二分查找
/// 索引 泛型
extension RandomAccessCollection {
public func binarySearch(for value: Iterator.Element, areInIncreasingOrder: (Iterator.Element, Iterator.Element) -> Bool) -> Index? {
guard !isEmpty else { return nil }
// 索引的距离一定是一个 整数(不一定是Int) 要确保 无效操作不能发生 (-1)
var left = startIndex
var right = index(before: endIndex)
while left < right {
let dist = distance(from: left, to: right)
let mid = index(left, offsetBy: dist/2)
let candidate = self[mid]
if areInIncreasingOrder(candidate, value) {
left = index(after: mid)
} else if areInIncreasingOrder(value, candidate) {
right = index(before: mid)
} else {
return mid
}
}
return nil
}
}
extension RandomAccessCollection where Iterator.Element: Comparable {
func binarySearch(for value: Iterator.Element) -> Index? {
return self.binarySearch(for: value, areInIncreasingOrder: < )
}
}
/// 简单介绍一下索引
/// 切片 查找
let a = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
let r = a[2..<5]
r.binarySearch(for: "c", areInIncreasingOrder: <)
洗牌算法 Fisher-Yates
/// 依然 依赖 整数泛型索引
//extension Array {
// mutating func shuffle() {
// for i in 0..<(count-1) {
// let j = Int(arc4random_uniform(UInt32(count-i))) + i
// //保证不予自己交换
// guard i != j else {
// return
// }
// self.swapAt(i, j)
// }
//
// }
//
// // shuffle 不可变版本
//
// func shuffled() -> [Element] {
// var clone = self
// clone.shuffle()
// return clone
// }
//}
/// 写一个 通用的 泛型索引 shuffled()
// 首先 要实现一个 arc4rodom 对应泛型索引
extension SignedInteger {
static func arc4random(_ upper_bound: Self) -> Self {
precondition(upper_bound > 0 && upper_bound < Int32.max, "arc4radom only callable uptr to \(UInt32.max)")
return numericCast(Darwin.arc4random_uniform(numericCast(upper_bound)))
}
}
extension MutableCollection where Self: RandomAccessCollection {
mutating func shuffle() {
var i = startIndex
let beforeIndex = index(before: endIndex)
while i < beforeIndex {
let dist = distance(from: i, to: endIndex)
let randomDistance = IndexDistance.arc4random(dist)
let j = index(i, offsetBy: randomDistance)
guard i != j else {
continue
}
self.swapAt(i, j)
formIndex(after: &i)
}
}
}
extension Sequence {
func shuffled() -> [Iterator.Element] {
var clone = Array(self)
clone.shuffle()
return clone
}
}
/// 例子
var numbers = Array(1...10)
numbers.shuffled()
numbers
///
使用泛型对代码进行代码设计
- 编写泛型函数,但是对特定类型提供不同算法,也可以通过 协议扩展编写同时作用于很多类型的泛型算法
- 泛型能帮助我们提取公共功能,减少模板代码
网络请求优化
let webserviceURL = NSURL.init(string: "www.baidu.com")
/// 最原始方式
struct User {
var name: Any
}
//
//func loadUsers(callback: ([User]?) -> ()) {
// let userURL = webserviceURL?.appendingPathComponent("/users")
// let data = try? Data(contentsOf: userURL!)
// let json = data.flatMap {
// try? JSONSerialization.jsonObject(with: $0, options: [])
// }
// let users = (json as? [String]).flatMap { jsonObjct in
// jsonObjct.compactMap(User.init)
// }
// callback(users)
//}
// 三种错误情况 URL 加载失败 JSON 解析失败 JSON构建对象失败
// 如果我们需要一个 新的请求 就要重写一遍 同时 很难测试 还是同步
// 优化1 -------- 提取user相关
func loadResource(at path: String, parse: (Any) -> A?, callback: (A?) -> ()) {
let resourceURL = webserviceURL?.appendingPathComponent(path)
let data = try? Data(contentsOf: resourceURL!)
let json = data.flatMap {
try? JSONSerialization.jsonObject(with: $0, options: [])
}
callback(json.flatMap(parse))
}
/// 重写loaduer
func loadUsers(callback: ([User]?) -> ()) {
loadResource(at: "/users", parse: jsonArray(User.init), callback: callback)
}
func jsonArray(_ transfrom: @escaping (Any) -> A?) -> (Any) -> [A]? {
return { array in
guard let array = array as? [Any] else {
return nil
}
return array.compactMap(transfrom)
}
}
/// 扩展 其他请求 就容易了
//func loadBlogPosts(callback: [Blog]? -> ()) {
// loadResource(at: "/blogs", parse: jsonArray({ jsonObjc in
// return User.init(name: jsonObjc as? String)
// }), callback: callback)
//}
/// 创建泛型数据类型 上面的parse 和 path 改变其中一个 另一个 也要改变 我们可以 把它们组装
struct Resource {
let path: String
let parse: (Any) -> A?
}
extension Resource {
func loadSynchronously(callback: (A?) -> ()) {
let resourceURL = webserviceURL?.appendingPathComponent(path)
let data = try? Data(contentsOf: resourceURL!)
let json = data.flatMap {
try? JSONSerialization.jsonObject(with: $0, options: [])
}
callback(json.flatMap(parse))
}
}
let userResouce: Resource<[User]> = Resource(path: "/users", parse: jsonArray(User.init))
/// 然后实现一个异步
extension Resource {
func loadAsynchronously(callback: @escaping (A?) -> ()) {
let resourceURL = webserviceURL?.appendingPathComponent(path)
let session = URLSession.shared
session.dataTask(with: resourceURL!) { (data, respose, error) in
let data = try? Data(contentsOf: resourceURL!)
let json = data.flatMap {
try? JSONSerialization.jsonObject(with: $0, options: [])
}
callback(json.flatMap(self.parse))
}.resume()
}
}
泛型的工作方式
从编译器角度看 泛型 是如何工作的
/// 标准库里的 min() 此时 编译器不知道 T 的大小 不知道 < 是否有重载(方法地址)
func min (_ x: T, _ y: T) -> T {
return y > x ? x : y
}
/// swift 使用 中间层 来解决这个 问题 当如到泛型是 会生成一个容器包裹泛型 如果泛型超出容器大小 就在堆上申请空间 容器包含 堆上的地址
/// 对于每一个泛型的参数 容器还包含一个或多个目击表 其中包含值目击表(wTable) 或者还有 协议目击表(vTable)用来运行时正确的派发实现
//// 伪代码实现
func min (_ x: Tbox, _ y: Tbox, valueTable: wTable, comparableTable: vTable) -> Tbox {
let xCopy = valueTable.copy(x)
let yCopy = valueTable.copy(y)
let result = comparableTable.lessThan(xCopy, yCopy) ? x : y
valueTable.release(xCopy)
valueTable.release(yCopy)
return result
}