今は 強参照循環
を打開するための個々の手段についてを見ているところで、予定としては今回は 無所有参照
に着目していくことになりますけれど。その前に、前回に眺めた 弱参照
について、それを裏方で管理している Weak テーブル
周りについても気にしてみると面白そうに思えてきました。とはいえ表からは窺いにくくて自分自身が把握できていないところなので、今回は少し暗中模索みたいな感じでそれを見ていってみようと思います。よろしくお願いしますね。
————————————————————————————————— 熊谷さんのやさしい Swift 勉強会 #230
00:00 開始 00:23 今回の展望 01:11 ウィークテーブルって知ってる? 03:48 思い浮かぶ、〇〇テーブル 03:57 ハッシュテーブルの概要 04:29 ウィットネステーブルの概要 05:08 仮想テーブルの概要 06:01 ウィークテーブルについて調べてみる 06:26 その前に Rua 言語とは? 07:29 Rua 言語のウィークテーブルについてはよく分からない 08:36 辞書で Key と Value を弱参照で持たせる 10:00 NSMapTable を使う 11:03 NSMapTable 使ってる? 12:10 NSMapTable は Foundation に所属 13:45 Objective-C からウィークテーブルを観察する 14:43 エキスパート Objective-C プログラミング 15:08 Amazon で本の目次を見るには? 16:44 Objective-C での weak の実現方法 18:54 ウィークテーブルとロックのあたり 24:07 参照先が解放されたときの処理 24:52 クロージング —————————————————————————————————
Transcription & Summarize : 熊谷さんのやさしい Swift 勉強会 #230
はい、じゃあ始めていきますね。今日は新たに無償予算書を見ていくという流れが本筋なわけですが、そこに行く前にもう少しWeakについて詳しく見ていったら面白いのかなと思ったので、それをやってみようかなという勉強会の時間です。
弱参照は前回見たとおりで、Weakを使うとオブジェクトの参照カウントを管理する際に、そのオブジェクトがなくなった時点で自動的にnilに置き換えられるというものです。前回までにスライドを見ながら話していた内容ですね。本来の流れは無償予算書ですが、その前に前回のWeakについてもう少し掘り下げます。
Weakを使うことで、参照がなくなれば自動的にnilをセットする内部的なシステムが組まれています。それを管理するシステムの中で「Weakテーブル」というものがあり、これにより弱参照が管理されています。このWeakテーブルの存在を知っている人は少ないかもしれません。
自分もWeakテーブルの存在を知ったきっかけは忘れましたが、調べるうちにそういったテーブルがあることがわかり、それがどう管理されているのか気になって軽く調べたことがありました。自分としては、このWeakテーブルを知っておくと、もっと良い感じに使えるのではないかと思います。
実際に使ってみた具体的な例としては、キャッシュ機構としてWeakを使う場合があります。例えば、C#ではConditionalWeakTableというものがあり、キーがWeakになっていて、そのキーが消滅すると一緒に値も消滅します。これについては実際に見たことはないので詳しくは分かりませんが、Swiftで自分で作ってみたことがありました。その時は無理やり作った感じでしたが、それをさらに詳しく知っているともっといい感じのものが作れるのではないかと思います。
テーブルというと、Weakテーブル以外に思い浮かぶものとしては、ハッシュテーブルがあります。ハッシュテーブルはキーをハッシュで持ち、値と関連付けることで検索性能を向上させるものです。また、Swiftではウィットネステーブル(witness table)というものがあり、プロトコル指向で使われています。このプロトコルにはどんなメソッドが含まれているかを管理するテーブルです。
さらに、オブジェクト指向で有名な仮想テーブル(Vテーブル)もあります。Vテーブルはオブジェクト指向でのメソッドの場所を管理し、オーバーライドされたメソッドを正しく呼び出すために使われます。基本的に、Swiftで押さえておくべきテーブルとしては、ウィットネステーブル、ウィークテーブル、そしてVテーブルを知っておけば大丈夫でしょう。
以上の内容を踏まえ、今日はWeakテーブルをさらに調べていこうと思います。 とりあえず、ウィークテーブルについて詳しい人がいれば教えてほしいと思っています。この用語がどれほど専門的なものか、少し疑問に感じます。
ルアという言語について話題が上がりました。ルアは確かスクリプト言語で、ゲームのスクリプトとして使われることが多いんですよね。手続き型で、オブジェクト指向やプロトタイプベースの要素も持ち合わせています。プログラミング言語がどのような問題を解決しようとしているのかを見るのは興味深いです。
さて、ルアについて話を進めましたが、本題のウィークテーブルと関係があるのかどうかは不明です。ウィークテーブルについてもう少し理解が深まるといいのですが。特に、SwiftのDictionaryでキーやバリューをウィーク参照にしたい場合にどうすればいいのかが気になります。
UIビューをキーにしてデータをバリューにするDictionaryを使いたいという話が出ていましたが、これはメモリーリークの問題を引き起こしそうですよね。UIKitのビューが解放されたときにDictionaryをメンテナンスしないと解放されなくなる可能性があります。キーとしてビューを使うとそういった問題が発生するでしょう。
その解決策としてNSMapTableが挙げられました。NSMapTableはキーとバリューのメモリー管理方法をウィークやストロングで選べるようになっており、iOS6やMac OS X 10.5から利用可能です。オブジェクトタイプを型パラメーターに指定して、イニシャライズのときにオプションを選ぶことができます。このオプションにはウィークやストロングがあります。
皆さんも使ったことがあるかもしれませんが、NSMapTableは便利ですね。ウィークとストロングの参照を柔軟に選べるため、メモリーリークの問題を防ぐことができます。オープンソースでも活用されており、特にメモリー管理において役立つツールです。 そういった需要が時々ありますよね。面白い、これは覚えておかなくてはやばいです。これは全然ウィークテーブルと関係ない収穫を得られました。これはどこに入っているんだろう?ファンデーションかな。ファンデーションですね。だから、Swiftはライブラリだから全然オッケーですね。本当に入っているのかなと思われるかもしれませんが、時々使っています。Swift時代なのか、Objective-C時代なのかについて言われますが、import Foundation
で、NSMapTable
に関するドキュメントをさっき見たところ、これはSwiftですね。当たり前のことですが、すごいですね。
これはこれとして置いといて、ウィークテーブルですね。Swiftで調べても良いものが出てこなかったのかな。ウィークマップテーブルですね。これを勉強会で取り上げてみるのも良いかもしれません。NSMapTableの類似品と思ったけれど、一応ありますね。でも、ちょっとこれは特徴付けがされているのかな?いや、これは置いといて、自分はこういったNSMapTable
のことを全く気にせずに使ったことがあります。走らないで使ったことがあるので、おまぬけな感じですね。
あとは、あんまりウィークテーブルについては出てこないけれど、確かにObjective-Cだともう少し表向きに出てくるのですよね。Objective-Cでやると、ウィークテーブルを管理するオプショナルなマップテーブルを管理する機能があります。Objective-Cのマップテーブルも紙版と一緒になっているからまたちょっと違うのかな、とりあえず目次を見たら面白そうな本だから見たかったけれど、置いといて、とりあえずここに載っているObjective-Cで困ったらこの本ですね。
オブジェクトについて、これを参照するウィーク変数として、WR
という専用の領域が用意されるらしいです。ウィーク変数を宣言すると、架空の領域としてオブジェクト型を扱うからIDサイズですね。ポインターですね。
代入するときに、object_storeWeak
関数があります。こういうふうにAPIがあるんですよね。ウィークテーブルを管理するための、自分が安着にイメージするだけだと思うんですけど、ストロング参照だったらリファレンスカウンターをアップするみたいな感じで、何かしらのマネジメントが必要なわけですけど、ウィーク参照の中に参照カウンターだけに注目しちゃうと何もしないわけですよね。だからついつい軽量なイメージを油断すると思っちゃうんですけど、ウィークのほうが値を監視して、なくなったら減らさないといけない都合で、共参照があるか同じように、逆参照があるかもちゃんと管理しないといけないので、それをウィークテーブルで管理していて、そのオーバーヘッドは普通にかかってくるよというところが、こういうAPIから伺えます。
自分から呼ばないですけどね、object_storeWeak
。これでunregisterNoLock
とregisterNoLock
の呼び出しが行われるんだって。長くなったと想像しにくいのと、この2つがどういう使い分けで両方呼ばれるのか分かりません。まだ分からないですけど、これで調べるのかな。weak_unregisterNoLock
、objc_weak_header
を見ると結構分かるのかもしれません。
ウィークテーブルからリムーブする、NoLock
が何かよく分からないけど、とにかくリムーブ。weak_registerNoLock
で追加するというふうですね。ロックって何なんだろう。NoLock
ロック。ウィークテーブルをロックしたい気がするけど、NoLock
なんですね。 スピンロック、スピンロックって何だったけ。勉強会で誰かに教えてもらった気がするのですが。ウィークテーブルはハッシュテーブルですよって書いてありますね。さて、スピンロックとは何だったでしょうか。単純にループで回して、定期的にロックを縮小しながら待つ方法です。
ああ、なるほど。そんな話を教えてもらったことがあります。これは「ビジーウェイト」の話ですね。でも、スピンロックは短期的に、ほぼ一瞬でリソースが開放される前提のときに普通に使えます。確か、ロックフリーの話もしたと思うんですが、あれは何だったけ…。
あとアトミック操作ですね。同時アクセス、そう、データレースを防ぐためにスピンロックを使ったりするのも普通に現実的な話です。
それから、Objective-Cのウィークテーブルはハッシュテーブルで管理されているらしいです。このハッシュテーブルにアトミックなロックが付いているかもしれないです。もう一度APIをノーロックで呼び出す場合、どうなるんだろう。ノーロックに失敗しないのかな。まあ、いいや。
とりあえずウィークテーブルに登録する関数を使って、ウィーク参照に値が入っているときはアンレジスターノーロックを呼び出してウィークテーブルから削除します。オブジェクトがnil
でなければ、ウィークレジスターノーロックを呼び出します。続いて、値がウィークテーブルから消された後、再度nil
じゃなければ登録する。これによってオブジェクトに対応する場所に参照を持たせて、ウィーク参照のポイントが追加されます。
値が入っているときは複数個含んでいるのかな。ちょっとこの説明だけだと全て理解するのは難しいですね。とにかくアンレジスターで削除し、nil
じゃなければウィークテーブルに登録することによって、ウィークテーブルからその参照をたどることができる状況になっているという感じです。
ある一つのオブジェクトに対するウィーク参照は複数存在可能です。その呼び出し時は、オブジェクトメッセージセンドのメッセージパッシングで呼び出され、途中でオブジェクトが解放されることがあります。そのため、アトミックにリテインする関数が用意されています。普通のリテインでは、リテインを呼び出した後に解放される可能性がありますが、アトミックにリテインすることで、データレースを防ぐことができるのですね。
とりあえず、しっかりとリテインする状況が用意されており、ロードウィークリテインはメンテナンスのマップテーブルにあったのと似たような話かもしれません。
ウィークポイントをリテインしてメッセージを送信し、オートリリースを仕掛けるのは面白いですね。それで、付属して利用するカウンターを管理し、解放される。これを見ておきたかったですね。オブジェクトの参照カウンターがゼロになったとき、クリアデュアルケーティングが呼び出され、すべてのウィークテーブルの参照先に特定の状態に変更されます。
つまり、オブジェクトが解放されるとウィーク参照が無効になるよという話。どのタイミングで開放されるとか知りたかったけど、ARCがリリースしてくれるAPIを呼ぶのでしょうか。そうかもしれないですね。
なるほど、他にもいろいろ解説はありますが、ウィーク参照を使うとウィークテーブルを管理する機構が動作するので、オーバーヘッドは一応覚えておくと将来に役立つかもしれませんね。
では、時間になったので今日はこれで勉強会を終わりにしましょう。お疲れ様でした、ありがとうございました。