2015-10-21 16 views
5

Trong ví dụ sau, tôi trình bày một UIViewController mà có một UIStackViewController như con của nó:UISplitViewController có lỗi lưu giữ vòng lặp trong iOS 9 không?

UIViewController *splitViewParentVC = UIViewController.new; 

UIViewController *masterVC = UIViewController.new; 
UIViewController *detailVC = UIViewController.new; 

UISplitViewController *splitViewController = [[UISplitViewController alloc] init]; 
splitViewController.viewControllers = @[masterVC, detailVC]; 

[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; 

[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"); 
      } 
     }); 
    }]; 
}); 

Trong iOS 9 và 9.1, các mã trên sẽ in the split view controller has leaked, chỉ ra rằng UIStackViewController đã bị rò rỉ (quan trọng hơn, nó cũng rò rỉ các bộ điều khiển chế độ xem chi tiết và chính của nó).

+0

Không xóa trẻ trước khi loại bỏ bộ điều khiển chia nhỏ khắc phục sự cố rò rỉ? – yuf

Trả lời

4

Có, lỗi chu kỳ lưu giữ là confirmed để tồn tại trong iOS 9 bởi nhân viên Apple.

Tôi đã thử nghiệm rằng chu kỳ lưu giữ không tồn tại trong iOS 8.4, nhưng không tồn tại trong iOS 9.0 và 9.1. Rò rỉ có vẻ như đã được sửa chữa như iOS 9.2 (được thử nghiệm trong Xcode 7.2 beta 2 trên iOS 9.2 Simulator) Tôi đã đặt cùng một sample project để dễ dàng xác nhận xem UISplitViewController có bị rò rỉ hay không (chỉ cần chạy và kiểm tra đầu ra console).

Điều này cũng kiểm tra một nỗ lực để cho phép bộ điều khiển chế độ xem chính và chi tiết được deallocated. Như người ta có thể thấy, bộ điều khiển chế độ xem chính dường như vẫn được giữ lại bởi UISplitViewController ngay cả sau khi nó được lấy ra khỏi thuộc tính mảng UISplitViewController.viewControllers.

Đây là mã từ dự án mẫu:

- (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"); 
       } 
      }); 
     }); 
    }); 
} 

UPDATE: Vụ rò rỉ dường như được cố định như iOS 9.2 (thử nghiệm trong Xcode 7.2 beta 2 về iOS 9.2 Simulator)

+0

Thú vị như thế nào bạn đã thử nghiệm nó trên iOS 9.4 khi 9.1 ra mắt hôm nay ... Typo? : P – erdekhayser

0

Như tôi biết -[UIViewController addChildViewController:] có vấn đề rò rỉ bộ nhớ trong iOS 9.0~9.1. Vì vậy, tôi nghĩ rằng nó không chỉ lỗi của UISplitViewController. Snipets như sau,

- (void)viewDidLoad { 
    [super viewDidLoad]; 
    MyFirstViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:@"MyFirstViewController"]; 
    [self addChildViewController:vc]; 
    [self.view addSubview:vc.view]; 
    [vc didMoveToParentViewController:self]; 
} 

Bạn sẽ thấy rằng giao dịch MyFirstViewController không được gọi nếu bạn rút khỏi bộ điều khiển chế độ xem hiện tại.

Cách giải quyết có thể là sử dụng Chế độ xem thùng chứa của bảng phân cảnh thay vì addChildViewController trong mã. Tôi đã comfirmed rằng bộ điều khiển xem con của Container View sẽ được phát hành đúng cách.

Cách giải quyết khác là removeChildViewController: trong -(void)viewDidDisappear:(BOOL)animated. Tuy nhiên, như nhân viên của apple mentioned, cách này không được khuyến nghị.

- (void)viewDidDisappear:(BOOL)animated { 
    [super viewDidDisappear:animated]; 
    NSArray<__kindof UIViewController *> * children = self.childViewControllers; 
    for (UIViewController *vc in children) { // not recommended 
     [vc willMoveToParentViewController:nil]; 
     [vc.view removeFromSuperview]; 
     [vc removeFromParentViewController]; 
    } 
} 
Các vấn đề liên quan