之前在使用RxSwfit的時(shí)候?qū)?rx這個(gè)東西的一直有些疑問筋现,不清楚實(shí)現(xiàn)原理唐础,趁著現(xiàn)在有些空余時(shí)間箱歧,打算深入研究下,于是有了這篇文章一膨。
通過學(xué)習(xí)呀邢,了解到rx其實(shí)就是一個(gè)命名空間的實(shí)現(xiàn),關(guān)于Swift命名空間的說明豹绪,參考喵神的這篇文章价淌。
關(guān)于命名沖突
Swift的命名空間是以模塊來劃分的,一個(gè)模塊表示一個(gè)命名空間瞒津,我們進(jìn)行APP開發(fā)是蝉衣,默認(rèn)添加到主target的內(nèi)容是同處于同一個(gè)命名空間的。如果用Cocoapod的方式導(dǎo)入的第三方庫巷蚪,是以一個(gè)單獨(dú)的target存在病毡,不會(huì)存在命名沖突。如果是以源碼的方式導(dǎo)入工程中屁柏,很有可能發(fā)生命名沖突啦膜。所以,為了安全起見淌喻,第三方庫都會(huì)使用命名空間這種方式來防止沖突僧家。在Objective-C上沒有命名空間,一般是使用方法名前面加前綴的方式避免沖突裸删。
命名空間原理
let text = usernameLabel.rx.text.orEmpty.asObservable()
如果你對RxSwift熟悉的話八拱,應(yīng)該看過不少類似的代碼。觀察上面的代碼涯塔,發(fā)現(xiàn)可以通過點(diǎn)語法訪問rx乘粒,這說明rx是一個(gè)屬性,rx又可以用點(diǎn)語法訪問UILabel的text屬性伤塌,說明rx是個(gè)數(shù)據(jù)結(jié)構(gòu)灯萍。
小結(jié):
1、rx是個(gè)數(shù)據(jù)結(jié)構(gòu)
2每聪、rx是個(gè)屬性
實(shí)現(xiàn)命名空間
有了上面的說明旦棉,我們可以定義一個(gè)名為ButtonNameSpace的命名空間結(jié)構(gòu)體
struct ButtonNameSpace {
func hello() {
print("Hello")
}
}
然后,我們?yōu)閁IButton這個(gè)類添加一個(gè)nameSpace計(jì)算屬性药薯,nameSpace返回一個(gè)ButtonNameSpace結(jié)構(gòu)體的實(shí)例
extension UIButton {
var nameSpace: ButtonNameSpace {
return ButtonNameSpace()
}
}
現(xiàn)在我們可以用如下方式使用
UIButton().nameSpace.hello() // 輸出hello
這樣的命名空間其實(shí)沒有作用绑洛,因?yàn)闊o法訪問UIButton的屬性和函數(shù)。
下面為ButtonNameSpace加上一些有用的東西
struct ButtonNameSpace {
private let button: UIButton
init(button: UIButton) {
self.button = button
}
func hello() {
print("Hello \(button.titleLabel?.text ?? "")")
}
}
修改UIButton的擴(kuò)展的實(shí)現(xiàn)
extension ButtonNameSpace {
var nameSapce: ButtonNameSapce {
return ButtonNameSapce(button: self)
}
}
現(xiàn)在命名空間更有意義了童本,因?yàn)樗鼘?chuàng)建它的對象真屯,有了一個(gè)引用。實(shí)際上穷娱,命名空間的真正目的是對創(chuàng)建它的對象保存一個(gè)引用绑蔫。
如果現(xiàn)在我想為UILabel也添加一個(gè)命名空間运沦,那么我還要為UILabel添加一個(gè)LabelNameSpace的命名空間,這樣顯然是不太聰明的做法配深,命名空間結(jié)構(gòu)體應(yīng)該是通用的携添,讓我們定義一個(gè)通用的命名空間結(jié)構(gòu)體。
實(shí)現(xiàn)泛型的命名空間
定義泛型的命名空間結(jié)構(gòu)體
struct MyNameSpace<Base> {
private let base: Base
init(base: Base) {
self.base = base
}
}
現(xiàn)在我們可以為UIButton和UILabel添加hello方法了
extension UIButton {
var nameSpace: MyNameSpace<UIButton> {
return MyNameSpace(base: self)
}
}
extension MyNameSpace where Base: UIButton {
func hello() {
print("Hi \(base.titleLabel?.text ?? "")")
}
}
let bnt = UIButton()
bnt("My button", for: .normal)
bnt.nameSpace.hello(). // 輸出:Hi My button
extension UILabel {
var nameSpace: MyNameSpace<UILabel> {
return MyNameSpace(base: self)
}
}
extension MyNameSpace where Base: UILabel {
func hello() {
print("Hi \(base?.text ?? "")")
}
}
觀察上面的代碼篓叶,為UIButton和UILabel添加擴(kuò)展的代碼基本是一樣的烈掠,都有nameSpace計(jì)算屬性,返回的都是命名空間結(jié)構(gòu)體的實(shí)例缸托,顯然我們可以用更優(yōu)雅的代碼實(shí)現(xiàn)上面的功能左敌,沒錯(cuò),就是協(xié)議擴(kuò)展俐镐。
協(xié)議擴(kuò)展
protocol NameSpaceWrappable {
associatedtype T
var rx: T { get }
static var rx: T.Type { get }
}
extension NameSpaceWrappable {
var rx: MyNameSpace<Self> {
return MyNameSpace<Self>(base: self)
}
static var rx: MyNameSpace<Self>.Type {
return MyNameSpace<Self>.self
}
}
觀察上面的代碼母谎,我們定義了一個(gè)NameSpaceWrappable的協(xié)議,包含一個(gè)泛型實(shí)例屬性和一個(gè)類屬性京革,分別返回MyNameSpace泛型結(jié)構(gòu)體的實(shí)例和類。
現(xiàn)在我們?yōu)閁IButton實(shí)現(xiàn)上述功能幸斥,只需讓UIButton遵循NameSpaceWrappable協(xié)議匹摇,就能得到一個(gè)名為rx的MyNameSpace結(jié)構(gòu)體的實(shí)例屬性和名為rx的類屬性。
extension UIButton: NameSpaceWrappable {}
let btn = UIButton()
btn.titleLabel?.text = "ni hao"
btn.rx.hello()
查看Reactive.swift的源碼甲葬,發(fā)現(xiàn)正是這樣實(shí)現(xiàn)的廊勃。
總結(jié)
同一個(gè)模塊中實(shí)現(xiàn)命名空間,其實(shí)就是通過類型嵌套限定使用范圍经窖。
水平有限坡垫,如有錯(cuò)誤的地方,請務(wù)必留言告訴我画侣。