什么是命名空间形式的扩展
什么是扩展?
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 所对应的类型,应该能满足以下需求:
v内部能够持有或者访问UIView对象。- 对于
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")
