如何给自己的库添加前缀

文章目录
  1. 1. 在 Rxswift中
  2. 2. 想法来源
  3. 3. 实现细节
    1. 3.1. 共享的代码
    2. 3.2. 具体扩展
    3. 3.3. 内存管理注意事项

在 Rxswift中

  • rx_tap : 原始语法The original syntax
  • rxTap : 同上,但是语义有歧义,如果不是 Rxswift库中却是用 rxABC 这样的
  • reactiveTap : 语义很清楚,但是太长了
  • rx.tap : ✨😲✨ 这样就很好了,一个漂亮的命名空间.

想法来源

LazySequence

1
2
myArray.map { ... }
myArray.lazy.map { ... }

我们也可以在 RxCocoa 中有同样的语法:

1
2
if myButton.enabled { ... }
myButton.rx.enabled.subscribeNext { ... }

实现细节

共享的代码

我想我们可以把 Reactive protocol 转换成一个泛型 struct :

1
2
3
4
5
6
public struct Reactive<Base: AnyObject> {
public let base: Base
public init(_ base: Base) {
self.base = base
}
}

接着我们扩展 NSObjectProtocol 协议来创建 rx 代理 :

1
2
3
4
5
public extension NSObjectProtocol {
public var rx: Reactive<Self> {
return Reactive(self)
}
}
RxSwift 中的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public struct Reactive<Base> {
/// Base object to extend.
public let base: Base
public init(_ base: Base) {
self.base = base
}
}

/// A type that has reactive extensions.
public protocol ReactiveCompatible {
/// Extended type
associatedtype ReactiveBase

@available(*, deprecated, message: "Use `ReactiveBase` instead.")
typealias CompatibleType = ReactiveBase

/// Reactive extensions.
static var rx: Reactive<ReactiveBase>.Type { get set }

/// Reactive extensions.
var rx: Reactive<ReactiveBase> { get set }
}

extension ReactiveCompatible {
/// Reactive extensions.
public static var rx: Reactive<Self>.Type {
get {
return Reactive<Self>.self
}
set {
// this enables using Reactive to "mutate" base type
}
}

/// Reactive extensions.
public var rx: Reactive<Self> {
get {
return Reactive(self)
}
set {
// this enables using Reactive to "mutate" base object
}
}
}

import class Foundation.NSObject
/// Extend NSObject with `rx` proxy.
extension NSObject: ReactiveCompatible { }

因为我们使用Self,所以我们需要扩展协议而不是具体类型。为了方便起见,我建议使用NSObjectProtocol,但我们也可以使用更有意义的协议,例如ReactiveCompatible或类似的协议。

自己实现一个小的扩展

使用 泛型(模板)创建自己的类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public struct Czw<Base> {
public let base: Base
public init(_ base: Base) {
self.base = base
}
}

public protocol CzwCompatible {
associatedtype CzwBase
static var czw: Czw<CzwBase>.Type { get set }
var czw: Czw<CzwBase> { get set }
}

extension CzwCompatible {
public static var czw: Czw<Self>.Type {
get {
return Czw<Self>.self
}
set {
// this enables using Czw to "mutate" base type
}
}

/// Czw extensions.
public var czw: Czw<Self> {
get {
return Czw(self)
}
set {
// this enables using Czw to "mutate" base object
}
}
}

import class Foundation.NSObject

extension NSObject: CzwCompatible { }

extension String: CzwCompatible {}

extension Czw where Base == String {
var x: String {
get {
"233"
}
}
}

let a = "1"
let b = a.czw.x

具体扩展

现在要将 reactive extensions 添加到类型,我们需要使用通用约束来扩展Reactive,而不是扩展目标类型本身。

因此对于下面这段代码:

1
2
3
4
5
extension UIButton {
public var rx_tap: ControlEvent<Void> {
return rx_controlEvent(.touchUpInside)
}
}

我们可以这么做

1
2
3
4
5
extension Reactive where Base: UIButton {
public var tap: ControlEvent<Void> {
return controlEvent(.touchUpInside)
}
}

内存管理注意事项

我看到的这种新语法的主要缺点是,它可能导致闭包内部不太直观的内存管理。
实际上,现在,响应式扩展属于Reactive类型(代理)。因此,无论在方法实现中使用 self 的什么地方,它都引用代理而不是 base
但是由于这个 proxy 是一种值类型,无法弱化(weakify)它,并且每次在闭包内使用 self 时,它实际上都会 strongify(retain)其所有引用类型成员(即 base )。
因此,在实现扩展时需要格外小心。 让我们以UISearchBar为例。

Sample 1 : 当前的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
extension UISearchBar {
public var rx_delegate: DelegateProxy {
return RxSearchBarDelegateProxy.proxyForObject(self)
}

public var rx_text: ControlProperty<String> {
let source: Observable<String> = Observable.deferred { [weak self] () -> Observable<String> in
let text = self?.text ?? ""

return (self?.rx_delegate.observe(#selector(UISearchBarDelegate.searchBar(_:textDidChange:))) ?? Observable.empty())
.map { a in
return a[1] as? String ?? ""
}
.startWith(text)
}

let bindingObserver = UIBindingObserver(UIElement: self) { (searchBar, text: String) in
searchBar.text = text
}

return ControlProperty(values: source, valueSink: bindingObserver)
}
}

Sample 2 : 错误内存管理的新实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
extension Reactive where Base: UISearchBar {
public var delegate: DelegateProxy {
return RxSearchBarDelegateProxy.proxyForObject(self.base)
}

public var text: ControlProperty<String> {
let source: Observable<String> = Observable.deferred { [weak searchBar = self.base] () -> Observable<String> in
let text = searchBar?.text ?? ""

return self.delegate.observe(#selector(UISearchBarDelegate.searchBar(_:textDidChange:)))
.map { a in
return a[1] as? String ?? ""
}
.startWith(text)
}

let bindingObserver = UIBindingObserver(UIElement: self.base) { (searchBar, text: String) in
searchBar.text = text
}

return ControlProperty(values: source, valueSink: bindingObserver)
}
}

Sample 3 : 具有良好内存管理的新实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
extension Reactive where Base: UISearchBar {
public var delegate: DelegateProxy {
return RxSearchBarDelegateProxy.proxyForObject(self.base)
}

public var text: ControlProperty<String> {
let source: Observable<String> = Observable.deferred { [weak searchBar = self.base] () -> Observable<String> in
let text = searchBar?.text ?? ""

return (searchBar?.rx.delegate.observe(#selector(UISearchBarDelegate.searchBar(_:textDidChange:))) ?? Observable.empty())
.map { a in
return a[1] as? String ?? ""
}
.startWith(text)
}

let bindingObserver = UIBindingObserver(UIElement: self.base) { (searchBar, text: String) in
searchBar.text = text
}

return ControlProperty(values: source, valueSink: bindingObserver)
}
}

关键部分是在每个 sample 代码的第 10行
sample2 很容易通过 self 访问 delegate 观察者,因为 delegate 是在同一 proxy 扩展上定义的。但是这样做,闭包捕获了 self 并保留了其所有引用成员。因此,它取消了 [weak searchBar = self.base] 捕获列表

避免这种情况的正确方法是,总是在旧的实现中用 base 替换 self ,并且每当有rx_前缀时,都应该用 rx. 代理替换它。

Move from rx_ prefix to a rx. proxy (for swift3 update ?)