swfit-泛型

和大多先进编程语言一样,swift拥有不少可以归类 泛型编程 的特性.

重载

拥有同样的名字,但是参数和返回类型不同的多个方法互相被称为重载方法,方法的重载不能称为泛型。不过和泛型类似我们可以将多种类型使用在同一接口。

  1. 自由函数的重载 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)
}

  1. 运算符的重载

使用操作符重载时,编译器会有一些奇怪的行为,既泛型的版本本来是更好的选择时,类型检查器还是会选择那些非泛型的重载。

// 定义一个幂运算**

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
  1. 使用泛型约束重载

判定一个数组的元素是否包含在另一个数组中,既是不是另一个子集。
标准库中有一个方法 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
}

你可能感兴趣的:(swfit-泛型)