2012-01-12 25 views
20

是否有任何理由在@interface申報私人伊維爾而不是@implementation@interface或@implementation中的私人伊娃爾

我看到這樣的代碼在互聯網上(包括Apple提供的文件):

foo.h中

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

Foo.m

@implementation Foo 
// do something with _foo 
@end 

的頭文件定義了一個類的公共接口,而私人ivar是...好...私人。那麼爲什麼不宣佈這樣呢?

foo.h中

@interface Foo : NSObject 
@end 

Foo.m在@implementation

@implementation Foo { 
@private 
    id _foo; 
} 

// do something with _foo 
@end 

回答

24

聲明實例變量是對象 - 的最新功能,這就是爲什麼你看到很多的代碼與他們在@interface - 沒有其他選擇。

如果您正在使用支持聲明的實例變量在執行聲明它們有可能是最好的默認編譯器 - 只把它們放在接口,如果他們需要被其他人訪問。

編輯:在實施申報的其他信息

實例變量隱含隱藏(有效私營)和能見度不能改變 - @public@protected@private不產生編譯器錯誤(與目前Clang至少)但被忽略。

+1

具體而言,有問題的編譯器似乎是Clang> 2.(現有的)GCC不會這樣做。 – 2012-01-12 19:18:59

+0

在這種情況下,'Clang'和'LLVM'是一樣的,對吧? – 2012-06-27 12:38:07

+0

@ranReloaded - no。有gcc - gcc front&backend,gcc-llvm - gcc前端,llvm後端 - 和clang - clang前端,llvm後端。我只在叮噹時進行測試,Josh在其中一個gcc上測試過。 YMMV,只需使用你正在使用的任何編譯器就可以看到。 – CRD 2012-06-27 19:10:42

4

如果您需要針對舊系統或Xcode版本的編譯器支持,則您會傾向於使用@interface

如果您確定不需要向後兼容性,那麼最好將它放在@implementation中。

  • 我認爲@private是一個很好的默認值。
  • 它最大限度地減少了編譯時間,並減少了依賴關係,如果你使用它的權利。
  • 您可以減少標題頂部的大部分噪音。許多人會爲他們的ivars放置#imports,但他們應該使用前向聲明作爲默認值。所以你可以從標題中刪除很多#imports和許多前向聲明。
3

的指令@public,@protected和@private是 在Objective-C沒有約束力,他們是編譯器提示的變量約 可訪問性。 它不限制您訪問它們。

例如:

@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); 
... 

gcc編譯器吐出:

的main.m:56:1:警告:實例變量「y」爲@private;這將是未來的硬錯誤

Main.m:57:1:warning:實例變量'y'是@private;這將是一個嚴重的錯誤,將來

每進行一次「innapropriate」訪問「私有」成員y上,但是編譯也無妨。

在運行的時候你

x = 10 , y = -10 

所以這真的是你不要寫訪問代碼這種方式,但由於objc是超的C, C語法工作得很好,所有的類是透明的。

您可以設置編譯器將這些警告視爲錯誤並保釋 - 但Objective-C不是在內部爲此類嚴格設置的。動態方法調度必須檢查每個調用的範圍和權限(slooooowwwww ...),因此,除編譯時警告外​​,系統期望程序員尊重數據成員範圍。

有幾個技巧來獲得objective-C中成員的隱私。 其中之一是確保將類的接口和實現分別放在單獨的.h和.m文件中,並將數據成員放在實現文件(.m文件)中。 然後,導入標頭的文件不能訪問數據成員,只能訪問類本身。 然後在標題中提供訪問方法(或不是)。如果需要,您可以在實現文件中實現setter/getter函數 以用於診斷目的,它們將被調用, ,但直接訪問數據成員將不會。

例如:

@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). 

... 

編譯器會發出這種明目張膽有心計的警告,但會繼續 和信任,你知道你在做什麼,你有你的理由(做(真的嗎?)您?)。 看起來像很多工作?容易出錯?耶嬰兒! 嘗試重構你的代碼,然後再訴諸如此類的魔術C技巧和肉丸手術。

但它確實存在。祝你好運。

+0

我會遠離iOS上的'double';) – 2012-06-27 12:39:54