バグの撃滅

一つ厄介なバグを潰したので、今後のために症状と対処法をメモしておく。
ThousandのTHDocument.nibでは、File's ownerであるTHDocument以外に、instanciateされているオブジェクトがあった。THBookmarkControllerである。ブックマークタブの全てを司るコントローラであり、そのプロパティはNSArrayController3つほどによってNSTableViewやNSPopUpButtonとBindされていた。

問題はTHDocumentがdealloc、つまり書類が閉じられてもTHBookmarkControllerがdeallocされないことだった。これのdeallocが呼ばれないと、表示中のスレッドリストの保存がされないし、なによりメモリーリークになる。ソースコード上のretain/releaseには問題はなし。原因はBindingにあるのではないかと推定された。

通常、THDocument.nib上のBindingがメモリーリークの原因になることはない。nibによって初期化されたオブジェクトは、Window Controllerかなにかによって適切にreleaseされ、Bindingも解除されるはずである。今回はそれが起こらなかった。何故か?

調べてみると、THBookmarkControllerのほかにもreleaseされないオブジェクトが存在した。それはスレッドリストのNSTableViewであったり、それとBindするためのNSArrayControllerであったりした。Viewが何らかの原因でretainされているために、それとBindするためのControllerも解放されないのだろうと見当がついた。

結論を言うと、この解放されないViewはTHBookmarkControllerによってawakeFromNib時にretainされていた。dealloc時にはreleaseされるようになっていたが、Viewが解放されない限りはそれにBindしているController、Modelも解放されないので意味がなかった。では何故THBookmarkControllerがこれをretainしていたかというと、2ペイン時と3ペイン時を切り替えるときにViewの入れ替えを行うためである。superViewを失ったViewを保持するために、最初からretainしていたわけだ。

結局何にもBindされていないTHDocumentにこれらのViewのretainを頼むことにして、THBookmarkControllerは正常に解放されるようになった。

  • 教訓:ModelがViewとBindされているときに、そのModelがそのViewをretainしてはいけない。