[swift 進階]讀書筆記-第九章:泛型 C9P1 重載

第九章:泛型 Generics

9.1 重載 Overloading

自由函數的重載 Overload Resolution for Free Functions

這一小段講的主要是泛型的重載相關知識點git

/// 泛型打印view的相關信息
///
/// - Parameter view: view
func log<View: UIView>(_ view: View) {
    print("It's a \(type(of: view)), frame: \(view.frame)")
}

///對泛型的重寫
func log(_ view: UILabel) {
    let text = view.text ?? "(empty)"
    print("It's a label, text: \(text)")
}

let label = UILabel(frame: .zero)
label.text = "liaoworking is handsome~~"
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 101))

log(label) //It's a label, text: liaoworking is handsome~~
log(button) //It's a UIButton, frame: (0.0, 0.0, 100.0, 101.0)
複製代碼

上面這個demo沒毛病對吧,只要咱們重載了一個泛型方法, 在打印對於的類型時就調用這個方法。 那咱們看看上面方法的下面的使用場景github

let views = [label, button] // Type of views is [UIView] for view in views {

for view in views {
    log(view)
}

/*
 It's a UILabel, frame: (20.0, 20.0, 200.0, 32.0)
 It's a UIButton, frame: (0.0, 0.0, 100.0, 50.0)
 */
複製代碼

咦~爲嘛在for循環中就沒法去區分方法了??? 手動黑人問號臉。swift

緣由:泛型的重載在編譯時期就肯定的。並非在runtime時候動態肯定。 就會有上面的差別。數組

運算符的重載 Overload Resolution for Operators

場景:咱們想要本身封裝一個冪運算的方法

precedencegroup tt{//優先級定義
    associativity: left //結合方向
    higherThan: MultiplicationPrecedence //高於乘法類型
}
infix operator **: tt
func **(lhs: Double, rhs: Double) -> Double {
    return pow(lhs, rhs)
}
func **(lhs: Float, rhs: Float) -> Float {
    return powf(lhs, rhs)
}
2*2.0**3.0
複製代碼
先寫一個冪運算的泛型demo
func **<i: SignedInteger>(lhs: i, rhs: i) -> i {
    let result = Double(lhs.max) ** Double(rhs.max)
    return numericCast(IntMax(result))
}

2**3 ///Error: Ambiguous use of operator
複製代碼

可在實際編譯過程當中卻編譯不過閉包

緣由: 對於重載運算符,編譯器會使用非泛型版本,不考慮泛型版本函數

解決: 至少將一個參數顯示的聲明爲Int類型,或者將返回值聲明爲Int類型便可性能

let intResult: Int = 2 ** 3 //8
複製代碼

這種編譯器的行爲只是對運算符生效,swift團隊對於性能上的考量選擇了這種能夠下降類型檢查器的複雜度的方案。ui

使用泛型約束進行重載

書中舉的例子是檢查一個集合是否是另一個集合的子集

swift標準庫中有一個方法isSubSet提供了這功能,但只是知足於Set這種知足了SetAlgebra協議的類型。spa

咱們本身去實現這個功能時注意函數的複雜度,for循環遍歷會使複雜度變成O(m * n).code

若是序列元素知足Hashable咱們能夠經過Set(array)數組轉化爲Set,再去用isSubSet去得到結果。

書中給出的最優解決方案是以下

///不須要是同一種類型 只須要other遵照Sequence的任意類型就能夠了,其複雜度也是成線性增加。

extension Sequence where Iterator.Element: Hashable {//泛型版本
func isSubset<S: Sequence>(of other: S) -> Bool
    where S.Iterator.Element == Iterator.Element {
        let otherSet = Set(other)
        for element in self {
            guard otherSet.contains(element) else {
                return false
            }
        }
        return true
}
複製代碼

這樣咱們寫的函數就有更多的可能性,能夠傳入一個數字的countableRange來進行檢查

[5,4,3].isSubSet(of:1...10)//true
複製代碼

使用閉包對行爲進行參數化---這小段的知識點對於函數的封裝有很大的啓發。

針對於上面的demo咱們二次引伸: 讓isSubset針對於不是Equatable的類型也適用, 咱們能夠傳一個返回值是bool的函數來代表元素是否相等。 swift標準庫中的contains方法就是這麼作的 其具體使用以下:

let isEven = {$0 % 2 == 0}
    (0..<5).contains(where:isEven) //true
    [1,3,5,7,9].contains(where:isEven) == false
複製代碼
咱們能夠相似於contain的使用去寫一個更靈活的isSubset
extension Sequence {
        func isSubset<S: Sequence>(of other: S,
                                   by areEquivalent: (Iterator.Element, S.Iterator.Element) -> Bool)
            -> Bool {
                for element in self {
                    guard other.contains(where: {areEquivalent(element, $0)}) else{return false}
                }
                return true
        }
    }
複製代碼

只要你提供閉包可以處理的比較操做,兩個序列的元素就算不是同種類型的也能夠進行對比。

let ints = [1,2]
let strings = ["1","2","3"]
ints.isSubset(of:Strings) { String($0) == $1 } //true
複製代碼

這一節知識點有點繁瑣。 不必一次性弄懂,多體會慢慢在實際項目中運用就能夠啦~

文章源文件地址,你們若是有更好的想法和觀點歡迎交流

相關文章
相關標籤/搜索