2010-01-13 32 views
35

Tôi là người mới sử dụng lập trình UI trên Mac và iPhone, và tôi đã chạy qua thứ gì đó khiến tôi hơi khó hiểu.Thứ tự khởi tạo UIViewController và tải

Một UIViewController có 3 phương pháp có liên quan đến việc khởi tạo của nó và xem nó:

  1. init (và các phương pháp init-like)
  2. loadView
  3. viewDidLoad (phương pháp đại biểu)

Tôi mong đợi những điều này xảy ra theo thứ tự ở trên. Đầu tiên UIViewController được cấp phát bởi một số đối tượng khác, sau đó init được gọi ngay lập tức (hoặc một số phương thức init khác, như initWithStyle).

Chỉ khi đối tượng được khởi tạo, tôi mong đợi nó sẽ gọi hàm loadView của riêng nó, sau đó chế độ xem, sau khi được tải, gọi phương thức ủy quyền viewDidLoad.

này không xảy ra, ví dụ:

@implementation UIViewControllerSubclass 

- (id)init { 
     NSLog(@"0"); 
    if (self = [super init]) { 
     NSLog(@"1"); 
    } 
    return self; 
} 

- (void)loadView { 
    [super loadView]; 
    NSLog(@"2"); 
} 

- (void)viewDidLoad { 
    [super viewDidLoad]; 
    NSLog(@"3"); 
} 

@end 

Tạo giao diện điều khiển đầu ra:

0 
2 
3 
1 

Các loadView và viewDidLoad phương pháp, do đó, không thể thực hiện cuộc gọi đại biểu, như các đại biểu thường là đặt sau khi gọi tới [super init], được gọi là sau loadView và viewDidLoad đã chạy:

UIViewControllerSubClass *someViewController = [[UIViewControllerSubclass alloc] init]; 
[viewController setDelegate:self]; 

Nếu tôi muốn chạy mã thiết lập ViewController theo một cách nào đó, thông báo cho đại biểu khi nó đi, mã có nên nằm trong phương thức init không? Không phải là lý do cho loadView tồn tại để cho phép mã như vậy được chạy vào thời điểm thích hợp? Dường như với tôi như tôi sẽ phải tạo một phương thức initWithDelegate mới, thiết lập ivar đại biểu trước khi gọi [super init], điều này có đúng không, hoặc tôi đang đi sai về điều này?

Cảm ơn trước :)

Trả lời

30

Quan điểm hệ thống nạp trên iPhone hoạt động như thế này:

Khi bạn khởi tạo một bộ điều khiển xem (hoặc với -init hoặc -initWithNibName: bundle :), nó không thực sự tạo và khởi tạo khung nhìn. Khi bạn gọi -view lần đầu tiên, nó gọi -loadView. Theo mặc định, -loadView chỉ tải chế độ xem từ tệp xib (nibName). Tuy nhiên, nếu bạn ghi đè điều này, bạn chịu trách nhiệm tạo chế độ xem và gán nó cho thuộc tính chế độ xem của trình điều khiển chế độ xem. Như một ví dụ:

- (void)loadView 
{ 
    UIView *view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]]; 
    // add subviews 
    self.view = view; 
    [view release]; 
} 

Mỗi khi bạn tạo ra các quan điểm, đó là khác biệt so với quan điểm ngày càng trở nên rõ ràng và hiển thị trên màn hình, nó gọi -viewDidLoad. (-viewDidAppear/-viewDidDisappear là dành cho chế độ hiển thị của chế độ xem trên màn hình)

Vì chúng tôi đã đi đúng hướng, hãy cân nhắc quản lý bộ nhớ. Khi chế độ xem bị tắt, hệ thống sẽ tự động đặt thuộc tính chế độ xem của bộ điều khiển chế độ xem thành không. Vấn đề là tất cả các bản xem trước của chế độ xem đó đều bị rò rỉ. Làm thế nào? Vâng, số lượng giữ lại cho mỗi lần xem phụ là 2 (lượt xem giữ lại các bản xem trước và bộ điều khiển chế độ xem của bạn có ổ cắm/ngà voi). Khi khung nhìn là nil, số lần giữ lại của chế độ xem đó là 1. Sẽ không có ý nghĩa nếu một chế độ xem được gắn xung quanh nếu chế độ xem không hiển thị, do đó bạn đặt nó thành nil trong -viewDidUnload (đây là một móc nối cho bất cứ khi nào chế độ xem được đặt thành nil).

+2

Lưu ý: viewDidUnload không còn được dùng như iOS 6.0 nữa. – Brainware

16

Phương pháp initWithNibName:bundle: là khởi tạo dành riêng cho lớp UIViewController.

Thử trọng và sử dụng nó thay vì init:

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { 
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { 
    } 
    return self; 
} 

...

UIViewControllerSubClass *someViewController = [[UIViewControllerSubclass alloc] initWithNibName:@"UIViewControllerSubclass" bundle:nil]; 
+1

Hm .. Khi tôi cố gắng chạy nó trong 5.1 Trình mô phỏng, trình gỡ lỗi không tiếp cận hoặc là '- [MyViewController initWithNibName: bundle:]' hoặc '- [MyViewController init]' –

+0

đây là nơi bạn khởi tạo UIViewController, như vậy, nhưng ** không ** nơi bạn đánh lừa với khung nhìn. – Fattie

4

gerry3 là đúng. Công cụ này vẫn còn làm tôi bối rối. Xem tài liệu trên designated initializers.

Cũng lưu ý rằng nếu bộ điều khiển của bạn được tạo bởi một ngòi được tải thì chỉ initWithCoder sẽ được gọi. loadView cũng không được gọi trong trường hợp đó.

Vì điều này có vẻ như hầu hết mã tôi đã thấy hầu hết khởi tạo trong nội dung như viewDidLoad mặc dù điều đó có vẻ sai, nhưng nó có vẻ là phương pháp tốt nhất được gọi trong cả hai trường hợp một nib và tạo lập trình.

Nhưng lý do này dường như không theo thứ tự là [siêu init] đang kêu gọi loadView vv -

+0

"hầu hết các mã tôi đã nhìn thấy hầu hết khởi tạo trong các công cụ như viewDidLoad mặc dù điều đó có vẻ sai" Trên thực tế nó _is_ sai. Lý do cho điều đó là chế độ xem của bạn có thể được tải xuống trong khi trình điều khiển chế độ xem của bạn sẽ vẫn ở xung quanh, sẵn sàng tải lại chế độ xem theo yêu cầu. Do đó, bạn có nguy cơ phải khởi tạo lại các biến của mình và trong một số trường hợp có thể dẫn đến các sự cố logic khó theo dõi trong ứng dụng của bạn. – KPM

0

Lấy @ đề nghị Nimrod của tôi đã làm một cái gì đó như:

-(void)viewDidLoad 
{ 
    // Init code here 
} 

Tôi không biết nếu điều này có thể gây ra các vấn đề với bộ nhớ bị rò rỉ nhưng nhìn vào tài liệu của Apple nó dường như không tạo ra bất kỳ chu kỳ:

view lifecycle http://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/Art/loading_a_view_into_memory.jpg

này được lấy từ: http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/ViewLoadingandUnloading/ViewLoadingandUnloading.html#//apple_ref/doc/uid/TP40007457-CH10-SW1

12
-(void)awakeFromNib 
{ 
} 

chỉ được gọi nếu bạn đang sử dụng bảng câu chuyện để lưu trữ ViewController được vẽ trong bảng câu chuyện Nib --- nghĩa là gói giao diện.

chuỗi thích hợp là

-(void)initWithCoder 
-(void)awakefromNib //(if story board is used) 
    or 
-(void)loadView----() //if manually generating the view contoller 

-(void)viewDidLoad-----(called only once in the life cycle of viewController) 
-(void)viewWillAppear 
-(void)viewDidAppear 

Trong khi di chuyển đến một ViewController

-(void)viewWillDisappear 
-(void)viewDidDisappear 

mới khi trở về ViewController đầu tiên

-(void)viewWillAppear 
-(void)viewDidAppear 
+0

loadView cũng được gọi nếu sử dụng bảng câu chuyện – malhal

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