Swift-泛型、关联类型

2022-08-22  本文已影响0人  Sweet丶
一、泛型

我们可以在函数、类、结构体、枚举中使用泛型,在名称后使用<T1, T2,...>来表示,例子如下:

// 交换两个变量的值:T1和T2是泛型
func swapValues<T1, T2>(_ a: inout T2, _ b: inout T1) {
    (a, b) = (b, a)
}

// 系统的可选类型中Wrapped是个泛型
public enum Optional<Wrapped> : ExpressibleByNilLiteral {
    case none
    case some(Wrapped)
}

Swift中泛型的实现原理:
如下图所示,除了参数外,还会传参数实际类型的metadata进去,metadata是参数的类型信息。

image.png
二、关联类型(Associated Type)

Swift协议中不可以用<T>来表示泛型,只能使用关联类型来做类型约束,相当于给类型一个占位名称,协议中可以有多个关联类型。
经典例子:下面是Alamofire中对于其内部方法调用.af的实现,ExtendedType是关联类型.

/// Protocol describing the `af` extension points for Alamofire extended types.
public protocol AlamofireExtended {
    /// Type being extended.
    associatedtype ExtendedType

    /// Static Alamofire extension point.
    static var af: AlamofireExtension<ExtendedType>.Type { get set }
    /// Instance Alamofire extension point.
    var af: AlamofireExtension<ExtendedType> { get set }
}

extension AlamofireExtended {
    /// Static Alamofire extension point.
    public static var af: AlamofireExtension<Self>.Type {
        get { AlamofireExtension<Self>.self }
        set {}
    }

    /// Instance Alamofire extension point.
    public var af: AlamofireExtension<Self> {
        get { AlamofireExtension(self) }
        set {}
    }
}

// 在使用时的方式:
1. 遵守协议AlamofireExtended:希望外部能通过.af来调用的类型应遵守该协议。
2.外部使用时就可以用xxx.af.xxxfunc

为什么协议中要使用关联类型而不是泛型来做类型约束?
有兴趣可以查看Swift 泛型协议

三、泛型\关联类型的进一步约束

可以对泛型或者关联类型做一些遵守协议或者类型的约束,比如HandyJson的下面代码。

// 这里的T代表泛型类型需要满足遵守HandyJSON协议
public class JSONDeserializer<T: HandyJSON> {
    // ...
}

// RxSwift中的关联类型的约束
public protocol SubjectType : ObservableType {
    associatedtype Observer: ObserverType

    func asObserver() -> Observer
}

也可以使用where来添加进一步做类型约束:

// RxSwift中的Throttle类的方法使用where来进一步做类型约束
final private class Throttle<Element>: Producer<Element> {
    override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) 
    where Observer.Element == Element {
        let sink = ThrottleSink(parent: self, observer: observer, cancel: cancel)
        let subscription = sink.run()
        return (sink: sink, subscription: subscription)
    }
}
四、不透明类型some
// ZLRunable1是个协议,这个方法会报错
func get(_ type: Int) -> ZLRunable1 {
    if type == 1 {
        return ZLCar()
    }
    return ZLHuman()
}

把一个带有关联类型的协议作为一个方法的返回值时,会报错:
Protocol 'ZLRunable1' can only be used as a generic constraint because it has Self or associated type requirements
即该协议只能作为一个针对泛型来遵守的约束,因为它内部使用了Self关联类型

要想返回值是该协议的类型,且不会报错的办法有两个:

func get2<T: ZLRunable1>(_ type: Int) -> T {
    if type == 1 {
        return ZLCar() as! T
    }
    return ZLHuman() as! T
}
protocol ZLRunable1 {
    associatedtype Speed
    var speed: Speed { get }
    // 协议的方法中有Self时,协议作为返回值也是会一样的错
//    static func ==(lhs: Self, rhs: Self) -> Bool
}

class ZLHuman: ZLRunable1 {
    // some也可以作为一个属性的类型修饰
    @available(iOS 13.0.0, *)
    var pets: some ZLRunable1 {
        return ZLCar()
    }
    
    var speed: Int = 4
}

class ZLCar: ZLRunable1 {
    var speed: Double = 3
}

@available(iOS 13.0.0, *)
func get(_ type: Int) -> some ZLRunable1 {
    return ZLHuman()
}

func testSome() {
    if #available(iOS 13.0.0, *) {
        var r1 = get(0)
        print(r1.speed)
        
        // 不透明类型屏蔽了真实类型内部的内容,协议外的内容访问会报错
        // 这句报错:Value of type 'some ZLRunable1' has no member 'pets'
        r1.pets
    }
}
/// 类型擦除
struct ZLRunable1Thunk<T>: ZLRunable1 {
    var speed: T {
        return _speed
    }
    private let _speed: T
    
    init<R: ZLRunable1>(_ runner: R) where R.Speed == T {
        _speed = runner.speed
    }
}

func get3(_ type: Int) -> ZLRunable1Thunk<Double> {

    let car = ZLCar()
    return ZLRunable1Thunk(car)
}
上一篇 下一篇

猜你喜欢

热点阅读