Swift
命名前缀
Swift
的命名空间是基于模块(module
),每一个module
代表了一个Swift
中的一个命名空间
// ModuleA.swift
// 这个文件存在于 ModuleA,非主工程 中
public class MyClass {
class func hello() {
print("hello from ModuleA")
}
}
// MyApp.swift
// 这个文件存在于 app 的主 target 中
class MyClass {
class func hello() {
print("hello from ModuleB")
}
}
如果我们需要在主工程(target
)中,调用非主工程里的ModuleA
里相同的函数hello
;我们只需要加上ModuleA
的target
名去访问即可。
// 使用哪个 MyClass,取决于是在哪个 target 中调用
ModuleA.MyClass.hello()
添加自己的命名空间
初级写法
声明协议Framable
public protocol Framable {
var x: CGFloat { get set }
var y: CGFloat { get set }
}
实现协议Framable
struct BaseFrame: Framable {
let view: UIView
init(view: UIView) {
self.view = view
}
public var x: CGFloat {
get { return view.frame.origin.x }
set { view.frame.origin.x = newValue }
}
public var y: CGFloat {
get { return view.frame.origin.y }
set { view.frame.origin.y = newValue }
}
}
实现UIView
命名前缀扩展
extension UIView {
public var cx: Framable {
return BaseFrame(view: self)
}
}
使用
self.view.cx.x = 10
进阶写法
以上固然可以实现命名空间前缀,但是还是不够优雅不够Swift
。
为此我们可以利用泛型和协议来更高效的实现命名空间
定义泛型结构体
定义一个泛型结构体,使用泛型Base
public struct CXKit<Base> {
let base: Base
init(_ base: Base) {
self.base = base
}
}
定义泛型协议
定义了一个 CXNameSpace
协议,这个协议代表了支持 namespace
形式的扩展。并利用extension
为这个协议 添加了默认实现。
public protocol CXNameSpace {
// associatedtype 相当于一个占位符,而不能表示具体的类型。具体的类型需要让实现的类来指定。
associatedtype T
var cx: CXKit<T> { get }
}
public extension CXNameSpace {
var cx: CXKit<Self> {
return CXKit(self)
}
static var cx: CXKit<Self>.Type {
return CXKit<Self>.self
}
}
实现命名空间cx
扩展
extension UIView: CXNameSpace { }
extension CXKit where Base: UIView {
public var x: CGFloat {
get { return base.frame.origin.x }
set { base.frame.origin.x = newValue }
}
}
使用
self.view.cx.x = 10
在对CXKit
做extension
时, where
后面的 Base
约束可以使用 ==
或者 :
区别是:
- 如果扩展的是值类型,比如
String,Date
等 ,就必须使用==
- 如果扩展的是类,则两者都可以使用,区别是如果使用
==
来约束,则扩展方法只对本类生效,子类无法使用。如果想要在子类也使用扩展方法,则使用:
来约束。