2009-08-03 33 views
53

Trong hầu hết các ví dụ tôi thấy các thiết lập sau đây IBOutlets:IBOutlet có cần phải là tài sản & tổng hợp không?



(Example A) 

FooController.h: 

@interface FooController : UIViewController { 
    UILabel *fooLabel; 
} 

@property (nonatomic, retain) IBOutlet UILabel *fooLabel; 

@end 

FooController.m: 

@implementation FooController 

@synthesize fooLabel; 

@end 

Nhưng cũng làm việc này tốt (chú ý: không có tài sản và không tổng hợp):



(Example B) 

FooController.h: 

@interface FooController : UIViewController { 
    IBOutlet UILabel *fooLabel; 
} 

@end 

FooController.m: 

@implementation FooController 

@end 

Có bất kỳ nhược điểm của việc xác định IBOutlets như trong ví dụ B? Giống như rò rỉ bộ nhớ? Dường như làm việc tốt và tôi không muốn để lộ IBOutlets như là tài sản công cộng vì chúng không được sử dụng như vậy, chúng chỉ được sử dụng trong việc thực hiện bộ điều khiển. Xác định nó ở ba nơi mà không cần một nhu cầu thực sự không tấn công tôi như rất DRY (Đừng lặp lại chính mình).

Trả lời

94

Trên Mac OS X, IBOutlets được kết nối như thế này:

  1. Hãy tìm một phương pháp được gọi là set <OutletName>:. Nếu nó tồn tại, hãy gọi nó.
  2. Nếu không có phương pháp nào tồn tại, hãy tìm một biến mẫu có tên <OutletName>, đặt nó mà không giữ lại.

Trên iPhone OS, IBOutlets được kết nối như thế này:

  1. gọi [đối tượng setValue: outletValue forKey: @ "<OutletName>"]

Hành vi của giá trị đặt cho khóa là làm một việc như sau:

  1. Tìm phương pháp có tên là < Tên cửa hàng >:. Nếu nó tồn tại, hãy gọi nó.
  2. Nếu không có phương pháp nào tồn tại, hãy tìm một biến mẫu có tên <OutletName>, đặt nó và giữ lại nó.

Nếu bạn sử dụng một thuộc tính, bạn sẽ rơi vào "Hãy tìm một phương pháp được gọi là set <OutletName>: ..." trường hợp trên cả hai nền tảng. Nếu bạn chỉ sử dụng một biến mẫu, thì bạn sẽ có hành vi giữ lại/giải phóng khác nhau trên hệ điều hành Mac OS X VS iPhone. Không có gì sai khi sử dụng một biến mẫu, bạn chỉ cần xử lý sự khác biệt này trong hành vi khi bạn chuyển đổi giữa các nền tảng.

Đây là liên kết đến tài liệu đầy đủ về chủ đề này. http://developer.apple.com/documentation/Cocoa/Conceptual/LoadingResources/CocoaNibs/CocoaNibs.html#//apple_ref/doc/uid/10000051i-CH4-SW6

+0

Xin chào Jon, Cảm ơn bạn đã trả lời chi tiết! Rất hữu ích –

+0

điều gì sẽ xảy ra nếu tên biến khác với tên thuộc tính? Có vấn đề gì nếu khác nhau? –

+0

Tên "OutletName" ở trên được định nghĩa là tên bên cạnh từ khóa "IBOutlet" trong mã nguồn. Nếu IBOutlet nằm trong @property, không có vấn đề gì về biến thể hiện được đặt tên kể từ khi thiết lập trình tìm kiếm. Nếu vì một lý do nào đó, một setter không tồn tại, sẽ có một ngoại lệ được nâng lên khi kết nối ổ cắm. Nếu từ khóa IBOutlet nằm trên biến mẫu, và trình đặt sẵn tồn tại với tên không khớp, trình thiết lập sẽ không được gọi. –

4

Kết quả cuối cùng là giống hệt nhau, nhưng bạn phải giữ một vài điều trong tâm trí:

  • Khi sử dụng instance fields như các cửa hàng, bạn không nên thả chúng trong dealloc.

  • Khi sử dụng tài sản mà có (giữ lại) thuộc tính, bạn phải giải phóng bất động sản trong dealloc (sử dụng self.property=nil hoặc bằng cách phát hành các biến ủng hộ). Điều này làm cho nó minh bạch hơn nhiều so với những gì đang xảy ra.

Trên thực tế nó tất cả đi xuống đến các quy tắc cũ: "ngươi sẽ phát hành những gì bạn alloc/giữ". Vì vậy, trong trường hợp bạn sử dụng một trường thể hiện như là ổ cắm, bạn đã không phân bổ/giữ lại nó, vì vậy bạn không nên phát hành nó.

+5

Lời khuyên này là chính xác cho Mac OS X chứ không phải hệ điều hành iPhone. Xem câu trả lời của tôi dưới đây. –

+1

Gọi "self.property = nil" trong phương thức dealloc không phải là cách hay để tham gia. Bạn không nên gọi các phương thức từ init hoặc dealloc, nếu bạn được phân lớp con, lớp con của bạn có thể không mong đợi những người định cư đó được gọi là durring sau khi được dealloced, hoặc trước khi nó được đưa vào. –

+0

Đó là cách duy nhất để giải phóng thuộc tính giữ lại tự động sử dụng tổng hợp biến thể (không có khai báo rõ ràng về trường sao lưu). Bạn không có lựa chọn, thực hành tốt hay không. –

12

Trên Mac OS X, IBOutlets không được giữ lại theo mặc định. Điều này ngược lại với hành vi trên hệ điều hành iPhone: trên hệ điều hành iPhone, nếu bạn không khai báo thuộc tính, nó sẽ được giữ lại và bạn phải giải phóng thuộc tính này theo phương thức dealloc. Ngoài ra, thời gian chạy 64-bit có thể tổng hợp các biến mẫu bằng cách sử dụng các khai báo thuộc tính. Điều đó có nghĩa là một ngày nào đó các biến mẫu (với IBOutlet) có thể bị bỏ qua.

Vì những lý do này, nó đồng nhất và tương thích hơn để luôn tạo thuộc tính và chỉ sử dụng số IBOutlet trong thuộc tính. Thật không may, nó cũng là tiết hơn.

Trong ví dụ đầu tiên của bạn, bạn luôn phải giải phóng ổ cắm theo phương pháp dealloc. Trong ví dụ thứ hai của bạn, bạn phải phát hành ổ cắm chỉ với hệ điều hành iPhone.

1

Có thể những ví dụ đó sử dụng mã giữ lại vì mã mẫu được phân bổ theo chương trình và khởi tạo UILabel và sau đó thêm nó vào UIView. Đó là trường hợp của nhiều ví dụ, vì việc học cách sử dụng Interface Builder thường không phải là vấn đề của họ.

Ví dụ thứ hai (không có thuộc tính và không tổng hợp) với IBOutlet được sử dụng khi nhà phát triển gán 'UILabel (Nút, Chế độ xem, v.v.) trong Trình tạo giao diện - bằng cách kéo IBOulet vào Nhãn hoặc Chế độ xem khác thành phần. Theo ý kiến ​​của tôi, hành động kéo và thả trước đó (Label on View) cũng thêm subview, Label vào View - và vân vân. Nhãn được giữ lại bởi một View; một View được giữ lại bởi Window; Cửa sổ được giữ lại bởi Chủ sở hữu tệp. Chủ sở hữu tệp thường là Tài liệu của bạn được khởi động chính.

Bạn sẽ lưu ý rằng khi bạn bước qua chương trình của bạn (bằng cách thêm một awakeFromNib

- (void)awakeFromNib 
{ 
    [fooLabel blahblah]; 
} 

rằng fooLabel đã có một địa chỉ bộ nhớ.

Thats vì Label được khởi tạo từ một gói tập tin (các nib tập tin) bằng cách sử dụng không init nhưng initWithCoder. Mà về cơ bản deserializes các dòng phim để một đối tượng - và sau đó đặt biến IBOutlet. (Chúng tôi vẫn đang nói về phương pháp IBOutlet.) (01). thod sử dụng phương pháp Giá trị khóa

call [object setValue:outletValue forKey:@"<OutletName>"] 

là mẫu Observer/Observable. Mẫu đó yêu cầu đối tượng Observable tham chiếu từng Observer trong một Set/Array. Một thay đổi về giá trị sẽ lặp lại Set/Array và cập nhật bằng nhau tất cả các Observers. Bộ đó S already đã giữ lại mỗi Máy quan sát do đó việc thiếu giữ lại trong iOS.

Hơn nữa và phần còn lại là đầu cơ.

Dường như trường hợp khi bạn sử dụng giao diện Builder sau đó

@property (nonatomic, retain) IBOutlet UILabel *fooLabel; 

có thể cần được thay đổi để

@property (nonatomic, weak) IBOutlet UILabel *fooLabel; 

hoặc @property (nonatomic, chuyển nhượng) IBOutlet UILabel * fooLabel;

Và sau đó nó không cần phải được phát hành trong một phương pháp dealloc. Plus nó sẽ đáp ứng các yêu cầu OSX và iOS.

Điều đó dựa trên logic và tôi có thể thiếu một số phần ở đây.

Tuy nhiên, có thể không quan trọng nếu lượt xem liên tục trong suốt thời gian tồn tại của chương trình của bạn. Trong khi một nhãn trong một hộp thoại phương thức (mở, đóng, mở, đóng) thực tế có thể bị giữ lại quá mức và bị rò rỉ trên mỗi chu kỳ. Và đó là bởi vì (đầu cơ một lần nữa) mỗi hộp thoại đóng được tuần tự hóa thành một hệ thống tập tin và do đó tồn tại x, y vị trí và kích thước, cùng với các phần phụ của nó, vv Và sau đó deserialized ... vào phiên kế tiếp mở (ngược với nói minimiz hoặc ẩn.)

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