什么是命名空间形式的扩展
什么是扩展?
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")