2010-03-21 64 views
1

我知道現代Objective-C運行時可以合成ivars。我認爲合成的ivars的行爲與標記爲@private的聲明的ivars完全相同,但它們沒有。聲明的私有ivars與合成的ivars有什麼不同?

因此,只有在我期望可以工作的現代運行時才能編譯代碼。例如,超:

@interface A : NSObject { 
#if !__OBJC2__ 
    @private 
    NSString *_c; 
#endif 
} 

@property (nonatomic, copy) NSString *d; 

@end 


@implementation A 

@synthesize d=_c; 

- (void)dealloc { 
    [_c release]; 
    [super dealloc]; 
} 

@end 

和子類:

@interface B : A { 
#if !__OBJC2__ 
@private 
    NSString *_c; 
#endif 
} 

@property (nonatomic, copy) NSString *e; 

@end 


@implementation B 

@synthesize e=_c; 

- (void)dealloc { 
    [_c release]; 
    [super dealloc]; 
} 

@end 

子類不能有相同名稱的聲明伊娃作爲其超類的一個聲明實例變量,即使父類的伊娃是私人的。在我看來,這似乎違反了@private的含義,因爲子類受到超類對隱私的選擇的影響。

然而,我更關心的是我應該如何考慮合成ivars。我認爲他們表現得像宣佈的私人ivars,但沒有脆弱的基礎類問題。也許這是對的,我只是不理解脆弱的基類問題。爲什麼上述代碼僅在現代運行時中編譯?當所有超類實例變量都是私有的時候,脆弱的基類問題是否存在?

回答

6

合成ivars 私人。你看到的是編譯器正常工作。

忽略!__OBJ2__條件中的代碼。我只會看看合成的ivars案例。

這是你的代碼有:

  • A::_c是合成的伊娃,是 只有 實施A中訪問。
  • B::_c是合成伊法爾的 ,並且在B的實施 內僅可獲得 。

它們不是同一個變量。它們不會相互碰撞,也不會存儲相同的值。

情況下仍然會出現一個問題...

如果你試圖把AB的在同一個文件執行,編譯器現在能夠看到的A::_c聲明同時正在編譯B並且將阻止您訪問B@synthesize e=_c;行中的_c

爲什麼要這樣做?我不是說A::_cB::_c是單獨的,無關的變量嗎?

在同一文件中有兩個實現產生了一種情況_c不是未聲明的標識符時,編譯器達到@synthesize e=_c;,所以編譯器不會嘗試創建一個新的綜合類伊娃的B,而是試圖訪問A::_c和失敗(因爲A::_c是私人的)。

+0

啊,所以在聲明private private ivars時會出現同樣的問題:'B.h'導入'A.h',聲明'_c'。隨後對「_c」的引用將被解釋爲對「A :: _ c」的引用,而不是未聲明的標識符。這是有道理的,儘管它似乎仍然是錯誤的。我希望符號的範圍僅限於超類,並且它將被視爲在其他地方未聲明。 – lemnar 2010-03-22 06:37:32

2

總之,你不能[有私人ivars]。

另一種方法是在包含所有狀態的實現文件中聲明一個類,然後將其視爲結構。

的.m:

@interface PrivateGoo:NSObject 
{... ivars ...} 
... props 
@end 

@implementation PrivateGoo 
... @synth or not 

然後,在.H,聲明一個實例變量等id privateGoop;。如果查找速度很糟糕,請在.m文件中使用((PrivateGoo*)privateGoop)->iVar;。如果你這樣做,要小心內存管理。

請注意,這有幾個優點,即使它乍看起來似乎很奇怪。也就是說,它使得重構PrivateGoo瑣碎;如果你的數據封裝突然發現需要有業務邏輯或更普遍可用,那麼這樣做是微不足道的。使用GC,編譯器將爲PrivateGoo制定佈局信息,並將完全自動管理所有內存。使用結構需要箍跳躍,使工作權的略帶一點 - 正確的分配模式等


對不起 - 實際上並沒有回答這個問題。

合成ivars的工作方式與普通ivars非常相似,只是您不必在頭文件中聲明'em'。除此之外,對他們沒有任何特別的私密性。請注意,合成的ivars不需要避免脆弱的基類問題。

@private,@protected@public指令爲編譯器的利益而存在;當你訪問被聲明爲不可訪問的東西時,給它一個警告(或錯誤)的鉤子。仍然有OBjective-C的相對平坦的名稱空間可以滿足,這就是阻止你在子類中擁有同名的iVar的原因。

脆弱的基類問題與屬性和合成的ivars是正交的。在iPhone OS和64位Mac OS X上的現代Objective-C 2.0 ABI中討論了易碎基類。出於二進制兼容性原因,32位Mac OS X保留了傳統行爲。

+0

我不能做什麼?我沒有試圖避免公開頭文件中公開頭文件的實現細節,也沒有解決遺留運行庫中的脆弱基類問題。我試圖避免與私有超類實例變量的名稱空間衝突。子類無法訪問私有ivars。但是,子類不能擁有與其超類相同名稱的私有ivar。由於上述代碼適用於現代運行時,合成的ivars確實允許這樣做。我認爲代碼在兩次運行時都會表現相同。這是'@ private'中的錯誤嗎?我不假設。爲什麼不? – lemnar 2010-03-21 19:22:32

+0

我想我現在開始明白了:我認爲'@ private'做了一些更復雜的事情,比如實際上隱藏了其他類的變量。不過,我仍然對爲什麼我的代碼在現代運行時下工作感到困惑。我的亞類不應該禁止合成與超級合成伊娃相同名稱的伊娃嗎?相反,它似乎是一個真正的私人(隱藏)伊娃。 – lemnar 2010-03-22 00:13:32

+1

編譯器禁止合成存儲位於超類中的屬性。因此,允許在超級和子類中合成具有相同名稱的ivars。 – bbum 2010-03-22 16:16:53