はい、iOS 9 にリテイン サイクル バグが存在することが Apple スタッフによって確認されています。
保持サイクルが iOS 8.4 には存在しないことをテストしましたが、iOS 9.0 および 9.1 には存在します。リークは iOS 9.2 の時点で修正されているようです (iOS 9.2 シミュレーターの Xcode 7.2 ベータ 2 でテスト済み) 。サンプル プロジェクトをまとめて、UISplitViewController 自体がリークするかどうかを簡単に確認しました (実行してコンソールを確認するだけです)。出力)。
これは、マスター ビュー コントローラーと詳細ビュー コントローラーの割り当て解除を許可する試みもテストします。ご覧のとおり、マスター ビュー コントローラーは、配列プロパティUISplitViewController
から削除された後でも保持されているようです。UISplitViewController.viewControllers
サンプル プロジェクトのコードは次のとおりです。
- (void)viewDidLoad {
[super viewDidLoad];
[self testSplitViewControllerRetainCycleWithCompletion:^{
[self testManuallyFreeingUpMasterAndDetailViewControllers];
}];
}
- (void)testSplitViewControllerRetainCycleWithCompletion:(void (^)())completion {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
UIViewController *splitViewParentVC = UIViewController.new;
UIViewController *masterVC = UIViewController.new;
UIViewController *detailVC = UIViewController.new;
UISplitViewController *splitViewController = [[UISplitViewController alloc] init];
splitViewController.viewControllers = @[masterVC, detailVC];
splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModeAllVisible;
splitViewController.preferredPrimaryColumnWidthFraction = 0.3125; // 320 / 1024
splitViewController.minimumPrimaryColumnWidth = 100;
[splitViewParentVC addChildViewController:splitViewController];
[splitViewParentVC.view addSubview:splitViewController.view];
[splitViewController didMoveToParentViewController:splitViewParentVC];
splitViewController.view.frame = splitViewParentVC.view.bounds;
splitViewController.view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
__weak UISplitViewController *wSplitViewController = splitViewController;
__weak UIViewController *wMaster = masterVC;
__weak UIViewController *wDetail = detailVC;
[self presentViewController:splitViewParentVC animated:YES completion:nil];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self dismissViewControllerAnimated:YES completion:^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (wSplitViewController) {
NSLog(@"the split view controller has leaked");
} else {
NSLog(@"the split view controller didn't leak");
}
if (wMaster) {
NSLog(@"the master view controller has leaked");
} else {
NSLog(@"the master view controller didn't leak");
}
if (wDetail) {
NSLog(@"the detail view controller has leaked");
} else {
NSLog(@"the detail view controller didn't leak");
}
completion();
});
}];
});
});
}
- (void)testManuallyFreeingUpMasterAndDetailViewControllers {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
UIViewController *splitViewParentVC = UIViewController.new;
UIViewController *masterVC = UIViewController.new;
UIViewController *detailVC = UIViewController.new;
UISplitViewController *splitViewController = [[UISplitViewController alloc] init];
splitViewController.viewControllers = @[masterVC, detailVC];
splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModeAllVisible;
splitViewController.preferredPrimaryColumnWidthFraction = 0.3125; // 320 / 1024
splitViewController.minimumPrimaryColumnWidth = 100;
[splitViewParentVC addChildViewController:splitViewController];
[splitViewParentVC.view addSubview:splitViewController.view];
[splitViewController didMoveToParentViewController:splitViewParentVC];
splitViewController.view.frame = splitViewParentVC.view.bounds;
splitViewController.view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
__weak UIViewController *wMaster = masterVC;
__weak UIViewController *wDetail = detailVC;
[self presentViewController:splitViewParentVC animated:YES completion:nil];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self dismissViewControllerAnimated:YES completion:nil];
splitViewController.viewControllers = @[UIViewController.new, UIViewController.new];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (wMaster) {
NSLog(@"the master view controller has STILL leaked even after an attempt to free it");
} else {
NSLog(@"the master view controller didn't leak");
}
if (wDetail) {
NSLog(@"the detail view controller has STILL leaked even after an attempt to free it");
} else {
NSLog(@"the detail view controller didn't leak");
}
});
});
});
}
更新: リークは iOS 9.2 で修正されたようです (iOS 9.2 シミュレーターの Xcode 7.2 ベータ 2 でテスト済み)。