2012-01-12 29 views
20

Có bất kỳ lý do nào để khai báo một ivar riêng trong @interface thay vì @implementation?ivar riêng trong @interface hoặc @implementation

tôi thấy mã như thế này khắp nơi trên internet (bao gồm tài liệu được cung cấp bởi Apple):

foo.h

@interface Foo : NSObject { 
@private 
    id _foo; 
} 
@end 

Foo.m

@implementation Foo 
// do something with _foo 
@end 

Các tập tin tiêu đề xác định giao diện công cộng của một lớp, trong khi một ivar tư nhân là ... tốt ... riêng tư. Vậy tại sao không tuyên bố nó như thế này?

foo.h

@interface Foo : NSObject 
@end 

Foo.m

@implementation Foo { 
@private 
    id _foo; 
} 

// do something with _foo 
@end 

Trả lời

24

Khai báo các biến Ví dụ trong @implementation là một tính năng gần đây của obj-C, đây là lý do tại sao bạn thấy rất nhiều mã với chúng trong @interface - không có lựa chọn nào khác.

Nếu bạn đang sử dụng trình biên dịch hỗ trợ khai báo các biến mẫu trong triển khai khai báo, có thể là mặc định tốt nhất - chỉ đặt chúng trong giao diện nếu chúng cần truy cập bởi người khác.

Edit: Thông tin bổ sung

biến Instance tuyên bố trong việc thực hiện là ngầm ẩn (có hiệu quả tư nhân) và khả năng hiển thị không có thể được thay đổi - @public, @protected@private không tạo ra lỗi biên dịch (với Clang hiện tại ít nhất) nhưng bị bỏ qua.

+1

Cụ thể, trình biên dịch được đề cập có vẻ là Clang> 2. (Hiện tại) GCC sẽ không làm điều đó. –

+0

Trong ngữ cảnh này, 'Clang' giống như 'LLVM', phải không? –

+0

@ranReloaded - no. Có gcc - gcc front & backend, gcc-llvm - gcc frontend, llvm backend - và clang - clang frontend, llvm backend. Tôi chỉ thử nghiệm trên clang, Josh thử nghiệm trên một trong những gcc của. YMMV, chỉ cần thử nó với bất kỳ trình biên dịch bạn đang sử dụng và xem. – CRD

4

Bạn sẽ ưu tiên @interface nếu bạn cần hỗ trợ trình biên dịch nhắm mục tiêu các hệ thống cũ hơn hoặc bản phát hành của Xcode.

Nếu bạn chắc chắn bạn sẽ không cần khả năng tương thích ngược, tôi muốn nói tốt nhất là đặt nó trong @implementation.

  • Tôi nghĩ @private là một mặc định tốt.
  • Nó giảm thiểu thời gian biên dịch và giảm phụ thuộc nếu bạn sử dụng đúng.
  • Bạn có thể giảm phần lớn tiếng ồn đó ở đầu tiêu đề của mình. Nhiều người sẽ đặt #imports cho ivars của họ, nhưng họ nên sử dụng một tuyên bố chuyển tiếp như mặc định. Vì vậy, bạn có thể xóa nhiều #imports và nhiều khai báo chuyển tiếp khỏi tiêu đề của mình.
3

Các chỉ thị @public, @protected, và @private là không ràng buộc trong Objective-C, họ là trình biên dịch gợi ý về khả năng tiếp cận của các biến. Nó KHÔNG HÃY ĐỪNG BẠN truy cập chúng.

dụ:

@interface Example : Object 
{ 
@public 
int x; 
@private 
int y; 
} 
... 


... 
id ex = [[Example alloc ] init]; 
ex->x = 10; 
ex->y = -10; 
printf(" x = %d , y = %d \n", ex->x , ex->y); 
... 

Trình biên dịch gcc spits ra:

Main.m: 56: 1: cảnh báo: Ví dụ biến ‘y’ là @private; đây sẽ là một lỗi khó khăn trong tương lai

Main.m: 57: 1: cảnh báo: biến cá thể ‘y’ là @private; đây sẽ là một lỗi khó khăn trong tương lai

một lần cho mỗi truy cập "ko hợp phá" để "private" viên y, nhưng biên dịch nó anyway.

Khi chạy bạn sẽ có được

x = 10 , y = -10 

Vì vậy, nó thực sự là tùy thuộc vào bạn KHÔNG phải viết mã truy cập theo cách này, nhưng vì objc là một superset của C, C cú pháp làm việc tốt, và tất cả các lớp trong suốt.

Bạn có thể đặt trình biên dịch xử lý các cảnh báo này là lỗi và bảo lãnh - nhưng mục tiêu-C không được thiết lập nội bộ cho loại độ nghiêm ngặt này. Phương thức động sẽ phải kiểm tra phạm vi và sự cho phép cho mỗi cuộc gọi (slooooowwwww ...), vì vậy ngoài một cảnh báo thời gian biên dịch, hệ thống mong đợi lập trình viên tôn trọng phạm vi thành viên dữ liệu.

Có một số thủ thuật để nhận quyền riêng tư của các thành viên trong mục tiêu-C. Một là đảm bảo rằng bạn đặt giao diện và triển khai lớp của bạn trong các tệp .h và .m riêng biệt, tương ứng và đặt các thành viên dữ liệu vào tệp triển khai (tệp .m). Sau đó, các tệp nhập tiêu đề không có quyền truy cập vào các thành viên dữ liệu, chỉ có chính lớp đó. Sau đó, cung cấp các phương thức truy cập (hoặc không) trong tiêu đề. Bạn có thể thực hiện các hàm setter/getter trong tệp triển khai cho các mục đích chẩn đoán nếu bạn muốn và chúng sẽ được gọi, nhưng quyền truy cập trực tiếp vào các thành viên dữ liệu sẽ không được.

dụ:

@implementation Example2 :Object 
{ 
//nothing here 
} 
double hidden_d; // hey now this isn't seen by other files. 
id classdata; // neither is this. 

-(id) classdata { return [classdata data]; } // public accessor 
-(void) method2 { ... } 
@end 

// this is an "informal category" with no @interface section 
// these methods are not "published" in the header but are valid for the class 

@implementation Example2 (private) 
-(void)set_hidden_d:(double)d { hidden_d = d; } 

// You can only return by reference, not value, and the runtime sees (id) outside this file. 
// You must cast to (double*) and de-reference it to use it outside of this file. 
-(id) hidden_d_ptr { return &hidden_d;} 
@end 

... 
[Main.m] 
... 
ex2 = [[Example2 alloc] init]; 

double d = ex2->hidden_d; // error: 'struct Example2’ has no member named ‘hidden_d’ 
id data = ex2->classdata; // error: 'struct Example2’ has no member named ‘classdata’ 
id data = [ex2 classdata] // OK 

[ex2 set_hidden_d : 6.28318 ]; // warning:'Example2' may not respond to '-set_hidden_d:' 

double* dp = [ex2 hidden_d_ptr]; // (SO UGLY) warning: initialization from incompatible pointer type 
           // use (double*)cast -- <pointer-to-pointer conversion> 
double d = (*dp); // dereference pointer (also UGLY). 

... 

Trình biên dịch sẽ phát đi cảnh báo cho shenanigans trắng trợn như vậy, nhưng sẽ đi trước và tin tưởng rằng bạn biết những gì bạn đang làm, và rằng bạn có lý do của bạn (làm (thực sự?) bạn?). Có vẻ như rất nhiều công việc? Lỗi dễ bị? Yay Baby! Hãy thử tái cấu trúc mã của bạn trước khi sử dụng các thủ thuật ma thuật C và phẫu thuật thịt viên như thế này.

Nhưng đúng vậy. Chúc may mắn.

+0

Tôi sẽ tránh xa 'double' trên iOS;) –