我们知道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() } } }
|
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) { }
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { } }
|
构建多个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
|