2010年11月28日日曜日

[iOS] UIViewControllerのdeallocが呼ばれなくてハマった

ふとしたきっかけでdeallocが呼ばれないUIViewControllerのサブクラスができてハマってしまいました。
小ネタですがメモしておきます。

[self.navigationController popViewControllerAnimated:YES];

popViewControllerAnimated:の裏側では popされたViewControllerがreleaseされてdeallocが呼ばれるはずです。

ところが

下のような構成のViewContorllerではpopされてもdeallocメソッドが呼ばれませんでした。
UITableViewControllerを使わずに、素のUIViewControllerのUIViewにUITableViewを埋め込もうとしています。
UIViewControllerとTableViwe間でやりとりをするために、お互いに相手のpropertyを持たせています。

@interface MyTableView : UITableView 
{
    MyViewController *myViewController;
}

@property (nonatomic, retain) MyViewController *myViewController;

@end

@interface MyViewController : UIViewController
{
    MyTableView *myTableView;
}

@property (nonatomic, retain) MyTableView *myTableView;

@end


@implement MyViewController

@synthesize myTableView;

- (void)loadView {
    [super loadView];

    self.myTableView = [[MyTableView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
    myTableView.myViewController = self;
    [self.view addSubview:myTableView];
}

- (void)dealloc { // なぜかこれが呼ばれない
    [myTableView release];
    [super dealloc];
}

@end
どうも
myTableView.myViewController = self;
このようにpropertyのpropertyにselfを指定してしまうとdeallocが呼ばれなくなるようです。

対処法としてはselfを指定しているpropertyにnilをセットすること。
- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidAppear:animated];

    if (self.parentViewController == nil) { // popされた時のみ実行される
        myTableView.myViewController = nil;
    }
}


delegateパターンでも同じようにnilをセットしてやる必要があるので注意です。




[2011/02/15 追記]

上のと似たような事例ですがまたdeallocが呼ばれない現象が発生したのでメモしておきます。
// NSArrayのプロパティにselfを格納する
self.dataSourceArray = [NSArray arrayWithObjects:self, obj1, obj2, nil];
NSArrayに(NSDictionaryも)値を入れるとその時点でretainCountがひとつ増えます。
なので上の事例と同じように、例えばviewDidDisappearなどで
dataSourceArray = nil;
としてやる必要があります。

0 件のコメント: