Swift 使用泛型实现命名空间形式的扩展

May 03, 2022 • 预计阅读时间 2 分钟

什么是命名空间形式的扩展

什么是扩展?

extension UIView {
    var v: ...
}

v 就是扩展出来的一个 UIView 属性。

什么是命名空间?

相较于 ObjC 没有命名空间的特性,Swift 使用模块名作为命名空间进行隔离,防止不同模块中有同名扩展而造成冲突。

但是同一模块中仍然会存在扩展同名冲突的问题,Swift 把这个问题交给开发者自己解决。

命名空间形式的扩展

RxSwift, SnapKit 都使用了命名空间形式的扩展,好处是可以把自己的方法集中管理,使用起来查找方便。

view.snp.makeConstraints {
    $0.height.equalTo(8)
    $0.width.equalTo(18)
}

和 SnapKit 相关的方法,都由 snp 这个扩展属性提供,snp 就是 SnapKit 的命名空间。

如何实现一个命名空间形式的扩展

首先定义一个泛型的协议:

protocol NamespaceProtocol {

    associatedtype T

    var v: T { get }
}

这样就定义一个命名空间 v,遵循了这个协议的类型就可以使用这个命名空间。

extension UIView: NamespaceProtocol {}

只要是 UIView 类型,就可以使用 v 命名空间了。

但是目前 v 还没有被实现,这应该是个什么类型呢?

v 所对应的类型,应该能满足以下需求:

  1. v 内部能够持有或者访问 UIView 对象。
  2. 对于 UIView 的子类,v 应该能扩展子类的方法,例如对于 UIImageView 可以访问其 image 属性。

对于需求 1,只需要在初始化的时候,传入对象就可以了。

对于需求 2,v 必须在传入时就能知道对象的类型,需要使用泛型。

明确了需求以后,就可以确定 v 的定义是一个泛型类型,可以是 class 或者 struct

class Namespace<T> {

    private var wrappedValue: T

    init(wrappedValue: T) {
        self.wrappedValue = wrappedValue
    }
}

为了满足需求 2,还需要给 NamespaceProtocol 添加默认实现:

extension NamespaceProtocol {

    var v: Namespace<Self> {
        Namespace(wrappedValue: self)
    }
}

v 的类型就会和子类一致,得益于 Swift 强大的泛型设计,默认实现可以让 Self 智能推断出子类类型(而且只有默认实现可以做到这一特性)。

要对 UIView 子类添加扩展方法,只需要扩展 Namespace 就行了:

extension UIView: NamespaceProtocol {}

extension Namespace where T: UILabel {}

extension Namespace where T: UIImageView {}

支持添加静态扩展方法

静态类型方法与实例方法相差不大,直接给代码:

protocol NamespaceProtocol {

    associatedtype T

    var v: T { get }

    static var v: T.Type { get }
}

extension NamespaceProtocol {

    var v: Namespace<Self> {
        Namespace(wrappedValue: self)
    }

    static var v: Namespace<Self>.Type {
        Namespace<Self>.self
    }
}

Namespace 添加一个实例方法和一个静态方法:

extension Namespace {

    func log(info: String) {

    }

    static func enableLog(_ enabled: Bool) {

    }
}

现在我们的命名空间既可以支持实例方法,也可以支持静态方法:

UIView.v.enableLog(true)
view.v.log(info: "init")
Swift
版权声明:如果转发请带上本文链接和注明来源。

lvv.me

iOS/macOS Developer

制作 Visual Studio 2022 离线安装包

Debian 手动设置 DNS 服务器地址