テーブルビューのスクロールで落ちることがある件の調査 (2)
プロジェクトテンプレートが片づいたので、例の「NSTableViewをスクロールしているとEXC_BAD_ACCESSで落ちる」件の調査。前回調べたとき(2012/04)までで、
- 落ちる場所は objc/OverrideMixin.mのovmix_ffi_closure()のrb_obj_is_kind_of()
- ここでObjC側からのメソッド呼び出しのパラメータをVALUEに変換したものが壊れてるぽい
ということがわかった。
ovmix_ffi_closure()はObjC側からRubyオブジェクトのメソッド呼び出しなので、どのメソッドのどのパラメータで起きているかをまず確認しよう。その結果、
- tableView:objectValueForTableColumn:row: (NSTableViewDataSource Protocol)
- tableView:willDisplayCell:forTableColumn:row: (NSTableViewDelegate Protocol)
のtableColumnでエラーになっていることがわかった。
調べやすくするため、単純にテーブルビューだけもつプログラムを作成。AppDelegateをこんな感じに。
attr_accessor :data def awakeFromNib self.data = Array.new 50_000.times do self.data << rand.to_s end end # NSTableViewDataSource def numberOfRowsInTableView(tableView) return self.data.size end def tableView_objectValueForTableColumn_row(tableView, column, row) return self.data[row] end # NSTableViewDelegate def tableView_willDisplayCell_forTableColumn_row(tableView, cell, column, row) return nil end # actions ib_action :startGC def startGC(sender) GC.stress = !GC.stress end
このプログラムで
- スクロールすると落ちることがある(1度だけ再現、すげー時間かかる)
- GC.stress下だとすぐ再現する。1つめのパラメータtableViewの処理中。
- キャッシュを無効にすると、GC.stress下でも(なかなか?)落ちない
- tableView, tableColumnをインスタンス変数に保持するとGC.stress下でも落ちない
ということになった。このことから
- VALUEがfree()されているにもかかわらず、キャッシュoc2rbCache(objc/ocdata_conv.m)に残ってしまう
というバグの可能性が高い、という感触。最初のプログラムでtableColumnの処理で落ちてた(tableViewでは落ちない)のは、tableViewはアウトレットになっているからだろう。
通常ObjC側からきたオブジェクトはGC時にキャッシュから消されるようになっている(objc/cls_objcid.m:_objcid_data_free())なので、どういうケースならこうなるかを考えているところ。
続く。