NSObject-Rxの実装を読み解く(二番煎じ)


NSObject-Rxの実装を紐解きます。

RxSwiftを利用していれば、おそらく利用したことのあるライブラリ、RxSwiftCommunity/NSObject-Rx

一応解説しておくと、NSObjectのサブクラスであれば、DisposeBagを宣言する必要なく利用することができるという便利な代物です。

RxSwiftで開発をしているととても良く使うライブラリの一つではないでしょうか。

今回、ひょんなことからこのライブラリがどのようにしてdisposeBagを実装しているのかを確認してみたくなって、びっくりしたので、まとめておきます。

Swiftの機能のみを利用した実装ではない。

今回話しているRxSwiftCommunity/NSObject-RXは、Swiftの機能のみを利用した実装にはなっていません。

NSObject-Rxの実体ファイルは下記ファイルなのですが、結構無茶苦茶やってますね。

大まかな流れは、

  1. Reactive Protocolのデフォルト拡張をBase: AnyClassに指定(すべてのクラスでデフォルト拡張される)
  2. Objective-Cのメンバ変数追加ができるAssociated Objectというびっくり黒魔術を利用してDisposeBagを宣言
    (SwiftでObjective-Cの黒魔術ってどうなった?: https://qiita.com/fmtonakai/items/e9036dec4af2609b5715)

  3. 初回アクセス時には生成、セット時にはsetter内部でセットされたdisposeBagをAssociated Object経由でプロパティかのように保持

  4. disposeBagの生成、アクセス処理はobjc_sync_enterobjc_sync_exitで強制同期。

結構すごい実装になってました。

ちなみになんですが、Associated ObjectはピュアSwiftだと動作しないようなので、Ubuntuとかで開発する場合は、うまくいかない感じだと思われます。

読み解いてみて

Objective-C由来の黒魔術がこんなところで生かされていたのが結構驚きでした。