Swift中的多功能名称空间
在Swift中,某些API(例如RxSwift)使用的技术将它们公开的代码限制在专用名称空间中。 在这篇文章中,我们将说明如何以最通用,最通用的方式完成此操作。
随时访问我的博客以阅读原始文章(语法突出显示更好)
let myButton = UIButton()
myButton.rx.tap.subscribe(…)
// this is a RxCocoa kind of code
您是否想过在Swift中编写这样的代码行是怎么可能的?
这是.rx。 乍一看似乎很尴尬的部分,不是吗? 它的行为就像某种自定义名称空间。
- 这是变量吗?
- 这是内部阶级吗?
我们有一些线索可以弄清楚:
- 由于可以通过点符号访问。 它必须是UIButton类的成员
- 它作为tap等一些属性,因此它是一个数据结构
通过这两个线索,我们可以尝试创建自己的名称空间。
在以下示例中,我们将尝试向UIButton添加自定义名称空间,以显示单个函数hello 。
由于名称空间必须是数据结构,因此我们尝试使用Swift结构:
struct ButtonNameSpace {
func hello () {
print ("Hello")
}
}
到目前为止,一切都很好。 由于名称空间应该是UIButton的成员,因此我们将其添加为计算属性:
extension UIButton {
var nameSpace: ButtonNameSpace {
return ButtonNameSpace()
}
}
现在,我们可以像这样使用它:
let myButton = UIButton()
myButton.nameSpace.hello()
调用的结果将是:“ Hello ”。
这可以用来做什么? 我不会说什么有趣的事情,因为在命名空间中,我们无法访问UIButton属性和函数。 我们将只做一些UIButton外部的事情,将一些上下文添加到我们的名称空间将是很棒的。
为了访问UIButton属性和函数,我们必须在此按钮上引用用于名称空间的数据结构。
struct ButtonNameSpace {
private let button: UIButtoninit(with button: UIButton) {
self.button = button
}func hello () {
let title = self.button.title(for: .normal) ?? ""
print ("Hello \(title)")
}
}
ButtonNameSpace结构在创建它的UIButton上保留一个引用。
我们仍然向UIButton添加一个计算属性,当创建ButtonNameSpace时,我们将对按钮本身的引用传递给它。 现在,我们可以访问按钮属性,例如title 。
extension UIButton {
var nameSpace: ButtonNameSpace {
return ButtonNameSpace(with: self)
}
}
我们仍然可以像这样使用它:
let myButton = UIButton()
myButton.setTitle("My button", for: .normal)
myButton.nameSpace.hello()
调用的结果将是:“ Hello My Button ”。 命名空间在这里更有意义,因为它与创建它的对象有关。
例如,如果我们必须为UIImage写一个命名空间该怎么办? 在实际方法中,我们还应该声明一个ImageNameSpace结构。
实际上,这种结构的真正目标是在创建它的对象上保留一个引用。 听起来仿制药可以参与其中吗? 不是吗
答案是肯定的 。 该结构必须是通用的。
struct MyNameSpace {
private let base: Baseinit(with base: Base) {
self.base = base
}
}
如我们所见,此结构的唯一目的是保留对作为init参数给出的内容的引用,以便我们可以在进一步的调用中对其进行访问。
现在,我们可以在Swift中使用真正很棒的东西: 条件扩展 。 仅当Base与我们想要的类型匹配时,它才允许我们仅向该结构添加功能。 例如,如果Base是一个UIButton,我们添加一个Hello函数,该函数将执行与旧ButtonNameSpace相同的操作 :
extension MyNameSpace where Base: UIButton {
func hello () {
let title = self.base.title(for: .normal) ?? ""
print ("Hello \(title)")
}
}
不用访问self.button.title ,我们可以访问self.base.title,因为我们知道base当然是一个UIButton。 我们可以通过考虑名称空间的通用性,向UIButton添加一个计算属性。
extension UIButton {
var myNameSpace: MyNameSpace {
return MyNameSpace(with: self)
}
}
用法和结果仍然相同。
let myButton = UIButton()
myButton.setTitle("My button", for: .normal)
myButton.myNameSpace.hello()
让我们回到UIImage的情况。 不再需要定义专用的名称空间结构,我们可以将通用的名称空间结构与新的条件扩展结合使用。
extension UIImage {
var myNameSpace: MyNameSpace {
return MyNameSpace(with: self)
}
}extension MyNameSpace where Base: UIImage {
func hello () {
let title = self.base.accessibilityHint ?? ""
print ("Hello \(title)")
}
}let myImage = UIImage()
myImage.accessibilityHint = "My Image"
myImage.myNameSpace.hello()
实际上,这正是RxSwift实现rx名称空间的方式,如下所示:Reactive.swift
希望这可以帮助。
敬请关注