美文网首页
Swift-泛型、关联类型

Swift-泛型、关联类型

作者: Sweet丶 | 来源:发表于2022-08-22 00:20 被阅读0次
一、泛型

我们可以在函数、类、结构体、枚举中使用泛型,在名称后使用<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关联类型

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

    1. 对该方法泛型 + 泛型遵守协议形式进行类型约束。
func get2<T: ZLRunable1>(_ type: Int) -> T {
    if type == 1 {
        return ZLCar() as! T
    }
    return ZLHuman() as! T
}
    1. some对返回值修饰, 然后方法内返回值只能是同一种类型, some@available(iOS 13.0.0, *)的。这个关键字的好处是可以做到只开放协议中的内容,比如下面的r1.doSomeThing()Person类内部的,调外部用会报错;
      它的弊端是只允许返回值是一个类型,如果该方法可能返回的是多个遵守该协议的类型,也是会报错Function declares an opaque return type, but the return statements in its body do not have matching underlying types
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
    }
}
  • 3.类型擦除Type Erase
    其实就是将ZLRunable1协议改为一个具体的遵守该协议的类型作为返回值,比如结构体、类,然后将在外部通过它调用方法时,转为实际类型的调用。
/// 类型擦除
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)
}

相关文章

  • Swift-泛型、关联类型

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

  • 14-泛型

    泛型(Generics) 泛型类型 关联类型(Associated Type) 类型约束 协议类型的注意点 泛型解...

  • Swift Tour Learn (十二) -- Swift 语

    本章将会介绍 泛型所解决的问题泛型函数类型参数命名类型参数泛型类型扩展一个泛型类型类型约束关联类型泛型 Where...

  • swift泛型

    泛型 泛型可以将类型参数化,提高代码复用率,减少代码量 使用泛型实现栈 关联类型(Associated Type)...

  • 泛型

    泛型(Generics) 泛型可以将类型参数化,提高代码复用率,减少代码量 泛型函数赋值给变量 关联类型(Asso...

  • 泛型(Generics)

    泛型可以将类型参数化,提高代码服用率,减少代码 类实现栈结构 结构体实现栈结构 结构体关联值使用泛型 关联类型(A...

  • Rust学习——关联类型

    所面对的问题:trait泛型 与 所关联的对象的具体类型,解决目标类型涉及泛型类别较多的情况,使代码更具可读性关联...

  • swift 泛型

    Swift-泛型学习和实例总结 - Mazy's Blog - CSDN博客 Swift中的泛型 - 简书

  • Swift — 泛型(Generics)

    Swift — 泛型(Generics) [TOC] 本文将介绍泛型的一些用法、关联类型、where语句,以及对泛...

  • Swift:泛型、高级运算符与扩展

    目录一,泛型二,高级运算符三,扩展 一,泛型 1,基本介绍 泛型是将类型参数化,提高代码复用率 2,关联类型 为协...

网友评论

      本文标题:Swift-泛型、关联类型

      本文链接:https://www.haomeiwen.com/subject/muwdgrtx.html