メソッドのhook?
RxCocoaではsentMessage
とmethodInvoked
というメソッドをフックするオペレータがあり、「ある関数が呼ばれたらこの処理をしたい」ということが実現できます。
sentMessage
ある関数が呼ばれる直前をhookすることができます。
例
(数値の順番通りに処理されます)
import UIKit
import RxSwift
import RxCocoa
extension UIViewController {
func doSomething() {
defer { print("3") }
print("2")
}
}
final class ViewController: UIViewController {
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
rx.sentMessage(#selector(UIViewController.doSomething))
.subscribe(onNext: { _ in
print("1")
})
.disposed(by: disposeBag)
doSomething()
print("4")
}
}
methodInvoked
ある関数の処理が終わった直後をhookすることができます。
例
(数値の順番通りに処理されます)
import UIKit
import RxSwift
import RxCocoa
extension UIViewController {
func doSomething() {
defer { print("2") }
print("1")
}
}
final class ViewController: UIViewController {
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
rx.methodInvoked(#selector(UIViewController.doSomething))
.subscribe(onNext: { _ in
print("3")
})
.disposed(by: disposeBag)
doSomething()
print("4")
}
}
面白いこと
sentMessage
とmethodInvoked
はそれぞれ関数に渡ってくる引数を取得することができます。戻り値がObservable<[Any]>
となっているため引数ラベルなどは使えませんが、引数が先頭から何番目かを指定することで引数を取得できます。
つまりどういうことなのか?
使い道としては指定した関数内の同期処理が完全に終わってるタイミングを保証できるのがmethodInvoked
です。なので、指定した関数内の処理後でないと特定のプロパティが更新されていない可能性があるなどのものは全てmethodInvoked
を使います。反対に指定した関数が呼ばれたタイミングで関数内の処理とは無関係に処理したい場合はsentMessage
を使います。
注意として指定した関数内に非同期処理があった場合にはそれは関数オブジェクトの生存期間と別管理となるため無視されます。sentMessage
とmethodInvoked
の内部実装ではSelectorが呼ばれるタイミングと関数がdeallocatedされたタイミングで通知を行なっているので関数がdeallocatedになっても処理が継続するものについては通常通りcompletion等で対応するしかありません。