2013-08-07 35 views
8

Tôi đang cố thực hiện khôi phục trạng thái trong ứng dụng sử dụng iOS 6+ và bảng phân cảnh nhưng tôi gặp sự cố khi tìm cách ngăn các cuộc gọi trùng lặp đến các phương thức nặng.Cuộc gọi vòng đời UIViewController kết hợp với khôi phục trạng thái

Nếu tôi chỉ cần khởi động ứng dụng, sau đó tôi cần phải thiết lập giao diện người dùng trong viewDidLoad:

- (void)viewDidLoad { 
    [super viewDidLoad]; 
    [self setupUI]; 
} 

này hoạt động tốt trong một bình thường, ngoài quốc doanh-phục hồi thế giới. Bây giờ tôi đã thêm phục hồi trạng thái và sau khi khôi phục một số tính tôi cần phải cập nhật giao diện người dùng với các thuộc tính:

- (void)decodeRestorableStateWithCoder:(NSCoder *)coder { 
    [super decodeRestorableStateWithCoder:coder]; 
    // restore properties and stuff 
    // [...] 
    [self setupUI]; 
} 

Vậy điều gì sẽ xảy ra bây giờ là lần đầu tiên phương pháp setupUI được gọi từ viewDidLoad, và sau đó một lần nữa từ decodeRestorableStateWithCoder:. Tôi không thấy một phương pháp mà tôi có thể ghi đè mà luôn được gọi là cuối cùng.

này được trình tự bình thường của phương pháp gọi:

  • awakeFromNib
  • viewDidLoad
  • viewWillAppear
  • viewDidAppear

Khi sử dụng khôi phục lại trạng thái, điều này được gọi là:

  • awakeFromNib
  • viewDidLoad
  • decodeRestorableStateWithCoder
  • viewWillAppear
  • viewDidAppear

tôi không thể đặt cuộc gọi đến setupUI trong viewWillAppear vì sau đó nó cũng sẽ được thực hiện mỗi khi bạn thuần trở lại một cái nhìn.

Sẽ dễ dàng hơn nhiều nếu decodeRestorableStateWithCoder được gọi là TRƯỚC viewDidLoad vì sau đó bạn có thể sử dụng các thuộc tính đã được khôi phục. Đáng buồn là không phải vậy, vì vậy ... làm thế nào tôi có thể ngăn chặn làm công việc trong viewDidLoad khi tôi biết rằng tôi cần phải làm điều đó trên một lần nữa trong decodeRestorableStateWithCoder ngay sau đó?

+2

Tôi muốn đặt giá trị boolean thành NO trong viewDidLoad và nếu KHÔNG, thì thực hiện các công cụ trong viewWillAppear. Tôi giả sử bạn không muốn làm điều đó theo cách này? –

+0

Thực sự có thể là giải pháp thực dụng nhất, vâng. Nó chỉ cảm thấy "sai" :) –

+0

@NitinAlabur không có lý do gì để đặt giá trị thành 'NO' trong' viewDidLoad' vì giá trị 'BOOL' mặc định. – k06a

Trả lời

3
@property (nonatomic) BOOL firstLoad; 

- (void)viewDidLoad { 
    [super viewDidLoad]; 
    self.firstLoad = YES; 
} 

- (void)viewWillAppear:(BOOL)animated { 
    [super viewWillAppear:animated]; 

    if (self.firstLoad) { 
     [self setupUI]; 
     self.firstLoad = NO; 
    } 
} 

Nhờ @calvinBhai cho đề xuất này.

+3

Tốt hơn để sử dụng giá trị nghịch đảo, như 'self.firstLoaded', giá trị mặc định là' NO' và bạn không có lý do để đặt giá trị đó trong 'viewDidLoad'. – k06a

6

Nếu bạn đang khôi phục trạng thái theo chương trình (tức là không sử dụng bảng phân cảnh), bạn có thể sử dụng + viewControllerWithRestorationIdentifierPath:coder:, khởi động trình điều khiển chế độ xem ở đó và sử dụng bất kỳ thứ gì bạn cần từ trình tạo mã để thực hiện khởi tạo trước viewDidLoad của bạn.

+ (UIViewController *)viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents coder:(NSCoder *)coder 
{ 
    if ([[identifierComponents lastObject] isEqualToString:kViewControllerRestorationIdentifier]) { 
     if ([coder containsValueForKey:kIDToRestore]) { 
      // Can only restore if we have an ID, otherwise return nil. 
      int savedId = [coder decodeIntegerForKey:kIDToRestore]; 
      ViewController *vc = [[ViewController alloc] init]; 
      [vc setThingId:savedId]; 
      return vc; 
     } 
    } 

    return nil; 
} 

Tôi nhận thấy rằng việc cố gắng triển khai khôi phục trạng thái đã hiển thị các thực tiễn lập trình xấu trong mã của tôi, như đóng gói quá nhiều vào viewDidLoad. Vì vậy, trong khi điều này làm việc (nếu bạn không sử dụng storyboards), các tùy chọn khác là để cấu trúc lại cách bạn đang thiết lập bộ điều khiển xem của bạn. Thay vì sử dụng một lá cờ, hãy di chuyển các đoạn mã thành các phương thức riêng của chúng và gọi những phương thức đó từ cả hai nơi.

+0

Đây phải là câu trả lời được chấp nhận. Ban đầu tôi không rõ ràng, nhưng bạn có quyền truy cập vào cùng một bộ mã hóa trong 'viewControllerWith ...' và 'decodeRestorableState ...'. Vì vậy, bạn có thể thiết lập bất kỳ dữ liệu cần thiết nào cho viewDidLoad trong 'viewControllerWith ...'. – arsenius

-1

Một chỉnh để MixedCase dòng chảy (mà là rất hữu ích, cảm ơn), dòng chảy cuộc gọi thực tế là một chút khác nhau:

này được trình tự bình thường của phương pháp gọi:

awakeFromNib

viewDidLoad

viewWillAppear

viewDidAppear

Khi sử dụng khôi phục lại trạng thái, điều này được gọi là:

viewControllerWithRestorationIdentifierPath (giải mã bất kỳ dữ liệu là cần thiết cho thường xuyên khởi động)

awakeFromNib

viewDidLoad

viewWillAppear

viewDidAppear

decodeRestorableStateWithCoder (decode dữ liệu restorable nhà nước, và thiết lập giao diện điều khiển của bạn)

+1

Điều này không chính xác. Thứ tự chính xác cho khôi phục trạng thái là: 'appWillLaunch -> awakeFromNib -> viewDidLoad -> decodeRestorableState -> appDidLaunch -> viewWillAppear' –

5

vui đủ trình tự giải mã thậm chí còn khác nhau và chính xác:

+viewControllerWithRestorationIdentifierPath:coder: 
awakeFromNib 
viewDidLoad 
decodeRestorableStateWithCoder: 
viewWillAppear 
viewDidAppear 

và nó hoàn toàn có ý nghĩa như thế này.

0

Có, nó thực sự sẽ đẹp hơn nếu -decodeRestorableStateWithCoder: được gọi trước -viewDidLoad. Thở dài.

tôi chuyển xem mã cài đặt của tôi (mà phụ thuộc vào trạng thái restorable) để -viewWillAppear: và sử dụng dispatch_once(), thay vì một biến boolean:

private var setupOnce: dispatch_once_t = 0 

override func viewWillAppear(animated: Bool) { 
    dispatch_once(&setupOnce) { 
     // UI setup code moved to here 
    } 

    : 
} 

Các tài liệu nói rằng "quan điểm không còn bị thanh trừng dưới bộ nhớ thấp điều kiện "vì vậy dispatch_once phải chính xác cho toàn bộ thời gian của bộ điều khiển chế độ xem.

+0

Vấn đề với dispatch_once trong trường hợp này là phần thân của khối dispatch_once sẽ chỉ được gọi một lần trong toàn bộ lời gọi của ứng dụng - do đó khi UIViewController được deallocated chính xác, chẳng hạn như được di chuyển ra khỏi một ngăn xếp UINavigationController và sau đó đẩy trở lại vào ngăn xếp, mã thiết lập của bạn sẽ không chạy lần thứ hai và xem sẽ không được thiết lập. Bạn phải RẤT cẩn thận với mẫu này và chỉ bao gồm mã trong khối dispatch_once mà không cần phải chạy nếu khung nhìn là deinit'd và được phục hồi. – Troy

0

Thêm vào câu trả lời của berbie,

Dòng chảy thực tế là:

initWithCoder 
+viewControllerWithRestorationIdentifierPath:coder: 
awakeFromNib 
viewDidLoad 
decodeRestorableStateWithCoder: 
viewWillAppear 
viewDidAppear 

Hãy nhận biết rằng bên trong initWithCoder, bạn cần phải thiết lập self.restorationClass = [self class]; này sau đó sẽ buộc viewControllerWithRestorationIdentifierPath:coder: được gọi.

2

Từ cuốn sách "Lập trình iOS 9: Lặn sâu vào xem, Xem kiểm soát, và Khung" trang 386-387

Trình tự được biết đến của các sự kiện trong phục hồi trạng thái là như thế này:

  1. application:shouldRestoreApplicationState:
  2. application:viewControllerWithRestorationIdentifierPath:coder:
  3. viewControllerWithRestorationIdentifierPath:coder:, để xuống chuỗi
  4. viewDidLoad, trong ord xóa chuỗi; có thể xen kẽ với các loại kể trên
  5. decodeRestorableStateWithCoder:, để xuống chuỗi
  6. application:didDecodeRestorableStateWithCoder:
  7. applicationFinishedRestoringState, để xuống chuỗi

Bạn vẫn không biết khi nào viewWillAppear:viewDidAppear: sẽ đến, hoặc cho dù viewDidAppear: sẽ đến. Nhưng trong applicationFinishedRestoringState bạn hoàn toàn có thể hoàn thành việc định cấu hình bộ điều khiển chế độ xem và giao diện của mình.

Các vấn đề liên quan