我们知道delegate在OC中是比较经常使用的代理模式。主要作用是将某对象的行为属性传递到另一个对象。以及绑定代理都用用weak弱引用。

1
2
3
4
5
6
7
8
9
10
11
protocol MyClassDelegate: class {
func doFoo()
}

class MyClass {
weak var delegate: MyClassDelegate?

func foo() {
delegate?.doFoo()
}
}

问题:假如我不用埋点方法替换foo等,在用户调用触发doFoo()方法,可以怎样做日志行为分析?前提是delegate又不能强制破坏或替换。
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
class MulticastDelegate<T> {

private let delegates: NSHashTable<AnyObject> = NSHashTable.weakObjects()

func add(_ delegate: T) {
delegates.add(delegate as AnyObject)
}

func remove(_ delegateToRemove: T) {
for delegate in delegates.allObjects.reversed() {
if delegate === delegateToRemove as AnyObject {
delegates.remove(delegate)
}
}
}

func invoke(_ invocation: (T) -> Void) {
for delegate in delegates.allObjects.reversed() {
invocation(delegate as! T)
}
}
}

class MyClassMulticastDelegate: MyClassDelegate {

private let multicast = MulticastDelegate<MyClassDelegate>()

init(_ delegates: [MyClassDelegate]) {
delegates.forEach(multicast.add)
}

func doFoo() {
multicast.invoke { $0.doFoo() } //调用代理对象的doFoo方法
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class AnalyticsEngine {}

extension AnalyticsEngine: MyClassDelegate {
func doFoo() {
print("Track foo event")
}
}

let logger = Logger()
let analyticsEngine = AnalyticsEngine()
let delegate = MyClassMulticastDelegate([logger, analyticsEngine])

let myClass = MyClass()
myClass.delegate = delegate

myClass.foo()

统计搜索文字点击按钮日志分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
final class SearchBarMulticastDelegate: NSObject, UISearchBarDelegate {

private let multicast = MulticastDelegate<UISearchBarDelegate>()

init(delegates: [UISearchBarDelegate]) {
super.init()
delegates.forEach(multicast.add)
}

func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
multicast.invoke { $0.searchBarSearchButtonClicked?(searchBar) }
}

func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
multicast.invoke { $0.searchBarCancelButtonClicked?(searchBar) }
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class SearchViewController: UIViewController {
let searchBar = UISearchBar()
}

class SearchResultsController: UIViewController, UISearchBarDelegate {
private unowned var svc: SearchViewController

init(_ svc: SearchViewController) {
self.svc = svc
super.init(nibName: nil, bundle: nil)
}

func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
// Show over `SearchViewController`
}

func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
// Hide from `SearchViewController`
}
}

构建多个delegate,组合组装成NSHashTable,再SearchBarMulticastDelegate内部实现每个delegate手动invoke调用方法。

1
2
3
4
5
6
7
8
9
10
11
extension AnalyticsEngine: UISearchBarDelegate {}
extension Logger: UISearchBarDelegate {}

let search = SearchViewController()

let logger = Logger()
let analyticsEngine = AnalyticsEngine()
let searchResults = SearchResultsViewController(search)

let searchBarMulticastDelegate = SearchBarMulticastDelegate(delegates: [logger, analyticsEngine, searchResults])
search.searchBar.delegate = searchBarMulticastDelegate

评论