一、泛型
我们可以在函数、类、结构体、枚举中使用泛型,在名称后使用<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
是参数的类型信息。
二、关联类型(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
}
- 加
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)
}
网友评论