相比于OC時代的完全沒有命名空間,Swift可以通過巧妙的辦法,實現幾乎等同于命名空間的效果。
需求
現在我們希望為UIColor
類增加一個擴展方法,根據其自身顏色生成圖像UIImage
。
在沒有命名空間的時代,可能結果是這樣子的:
let image = UIColor.blue.image
或者為避免與其它框架API同名,可能結果會變成這樣子:
let image = UIColor.blue.jx_image
當然這種風格非常地OC。
那有沒有優雅一點的辦法來代替OC Style?比如能寫成這樣子就好了:
let image = UIColor.blue.jx.image
Swift3繞一下路,也是可以做到的。姑且把中間的.jx
叫做命名空間(Namespace),沒錯,抄了一下C#們的叫法。
定義命名空間
具體的做法通過擴展特定的協議來達到目的。
我們需要定義兩個協議,其中一個是固定的寫法:
/// 類型協議
protocol TypeWrapperProtocol {
associatedtype WrappedType
var wrappedValue: WrappedType { get }
init(value: WrappedType)
}
struct NamespaceWrapper<T>: TypeWrapperProtocol {
let wrappedValue: T
init(value: T) {
self.wrappedValue = value
}
}
另一個協議基本上也是固定的寫法,只是根據我們的需要,更改其內的命名空間名字即可。
比如我需要一個名為jx
的命名空間,就可以這樣寫:
/// 命名空間協議
protocol NamespaceWrappable {
associatedtype WrapperType
var jx: WrapperType { get }
static var jx: WrapperType.Type { get }
}
extension NamespaceWrappable {
var jx: NamespaceWrapper<Self> {
return NamespaceWrapper(value: self)
}
static var jx: NamespaceWrapper<Self>.Type {
return NamespaceWrapper.self
}
}
需要什么樣的命名空間,就把其中的jx
改成你希望的名字。
使用命名空間
實際上我們需要擴展的類不是UIColor
,而是協議。
這里分兩步,第一步是讓需要擴展的類遵循協議NamespaceWrappable
:
extension UIColor: NamespaceWrappable {}
第二步是擴展協議TypeWrapperProtocol
:
extension TypeWrapperProtocol where WrappedType == UIColor {
/// 用自身顏色生成UIImage
var image: UIImage? {
let rect = CGRect(x: 0, y: 0, width: 1, height: 1)
UIGraphicsBeginImageContext(rect.size)
let context = UIGraphicsGetCurrentContext()
context?.setFillColor(wrappedValue.cgColor)
context?.fill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
return image
}
}
在擴展的方法中,self
代表的是協議,而不是目標擴展類。那要用到self
怎么辦?用wrappedValue
吧,它就是我們的目標類/實例。
驗證
現在可以愉快地玩耍了:
let image = UIColor.blue.jx.image
使用了命名空間后,還有一個優點就是可以利用Xcode的代碼補全。

Xcode代碼補全
相比在打出.im
后Xcode會彈出一大堆匹配到im
兩字母的方法,使用了命名空間后,點出.jx.
時,Xcode就只會提示此命名空間內的方法/屬性。特別是String
和UIView
這種有巨量方法的類,只要打出命名空間就可以快速過濾不需要的方法。