1. 程式人生 > >@synthesize與@dynamic

@synthesize與@dynamic

  1. @property有兩個對應的詞,一個是 @synthesize,一個是 @dynamic。如果 @synthesize和 @dynamic都沒寫,那麼預設的就是@syntheszie var = _var; 

  2. 在Xcode4.5和以後的版本中,可以省略@synthesize,編譯器會自動加上setter和getter方法的實現。並且預設會去訪問_age這個成員變數,如果找不到_age這個成員變數,會自動生成一個叫做_age的私有成員變數。

  3. @synthesize 的語義是如果你沒有手動實現 setter 方法和 getter 方法,那麼編譯器會自動為你加上這兩個方法。

  4. @dynamic 告訴編譯器:屬性的 setter 與 getter 方法由使用者自己實現,不自動生成。(當然對於 readonly 的屬性只需提供 getter 即可)。假如一個屬性被宣告為 @dynamic var,然後你沒有提供 @setter方法和 @getter 方法,編譯的時候沒問題,但是當程式執行到 instance.var = someVar,由於缺 setter 方法會導致程式崩潰;或者當執行到 someVar = var 時,由於缺 getter 方法同樣會導致崩潰。編譯時沒問題,執行時才執行相應的方法,這就是所謂的動態繫結。

  5. 宣告關聯物件屬性的時候用的比較多,要自己實現setter和getter。

14. @synthesize合成例項變數的規則是什麼?假如property名為foo,存在一個名為_foo的例項變數,那麼還會自動合成新變數麼?

在回答之前先說明下一個概念:

例項變數 = 成員變數 = ivar

這些說法,筆者下文中,可能都會用到,指的是一個東西。

正如 Apple官方文件 You Can Customize Synthesized Instance Variable Names 所說: enter image description here

如果使用了屬性的話,那麼編譯器就會自動編寫訪問屬性所需的方法,此過程叫做“自動合成”( auto synthesis)。需要強調的是,這個過程由編譯器在編譯期執行,所以編輯器裡看不到這些“合成方法” (synthesized method)的原始碼。除了生成方法程式碼之外,編譯器還要自動向類中新增適當型別的例項變數,並且在屬性名前面加下劃線,以此作為例項變數的名字。

@interface CYLPerson : NSObject 
@property NSString *firstName; 
@property NSString *lastName; 
@end

在上例中,會生成兩個例項變數,其名稱分別為 _firstName 與 _lastName。也可以在類的實現程式碼裡通過 @synthesize語法來指定例項變數的名字:

@implementation CYLPerson 
@synthesize firstName = _myFirstName; 
@synthesize lastName = _myLastName; 
@end 

上述語法會將生成的例項變數命名為 _myFirstName 與 _myLastName ,而不再使用預設的名字。一般情況下無須修改預設的例項變數名,但是如果你不喜歡以下劃線來命名例項變數,那麼可以用這個辦法將其改為自己想要的名字。筆者還是推薦使用預設的命名方案,因為如果所有人都堅持這套方案,那麼寫出來的程式碼大家都能看得懂。

總結下 @synthesize 合成例項變數的規則,有以下幾點:

  1. 如果指定了成員變數的名稱,會生成一個指定的名稱的成員變數,

  2. 如果這個成員已經存在了就不再生成了.

  3. 如果是 @synthesize foo; 還會生成一個名稱為foo的成員變數,也就是說:

如果沒有指定成員變數的名稱會自動生成一個屬性同名的成員變數,

  1. 如果是 @synthesize foo = _foo; 就不會生成成員變量了.

假如 property 名為 foo,存在一個名為 _foo 的例項變數,那麼還會自動合成新變數麼? 不會。如下圖:

enter image description here

15. 在有了自動合成屬性例項變數之後,@synthesize還有哪些使用場景?

回答這個問題前,我們要搞清楚一個問題,什麼情況下不會autosynthesis(自動合成)?

  1. 同時重寫了 setter 和 getter 時
  2. 重寫了只讀屬性的 getter 時
  3. 使用了 @dynamic 時
  4. 在 @protocol 中定義的所有屬性
  5. 在 category 中定義的所有屬性
  6. 過載的屬性

當你在子類中過載了父類中的屬性,你必須 使用 @synthesize 來手動合成ivar,過載了屬性會包警告,使用@synthesize後通過列印IvarList發現子類也生成了一個父類同名的成員變數,子類父類都有了

除了後三條,對其他幾個我們可以總結出一個規律:當你想手動管理 @property 的所有內容時,你就會嘗試通過實現 @property 的所有“存取方法”(the accessor methods)或者使用 @dynamic 來達到這個目的,這時編譯器就會認為你打算手動管理 @property,於是編譯器就禁用了 autosynthesis(自動合成)。

因為有了 autosynthesis(自動合成),大部分開發者已經習慣不去手動定義ivar,而是依賴於 autosynthesis(自動合成),但是一旦你需要使用ivar,而 autosynthesis(自動合成)又失效了,如果不去手動定義ivar,那麼你就得藉助 @synthesize來手動合成 ivar。

其實,@synthesize 語法還有一個應用場景,但是不太建議大家使用:

可以在類的實現程式碼裡通過 @synthesize 語法來指定例項變數的名字:

@implementation CYLPerson 
@synthesize firstName = _myFirstName; 
@synthesize lastName = _myLastName; 
@end 

上述語法會將生成的例項變數命名為 _myFirstName 與 _myLastName,而不再使用預設的名字。一般情況下無須修改預設的例項變數名,但是如果你不喜歡以下劃線來命名例項變數,那麼可以用這個辦法將其改為自己想要的名字。筆者還是推薦使用預設的命名方案,因為如果所有人都堅持這套方案,那麼寫出來的程式碼大家都能看得懂。

舉例說明:應用場景:

//
// .m檔案
// http://weibo.com/luohanchenyilong/ (微博@iOS程式犭袁)
// https://github.com/ChenYilong
// 開啟第14行和第17行中任意一行,就可編譯成功

@import Foundation;

@interface CYLObject : NSObject
@property (nonatomic, copy) NSString *title;
@end

@implementation CYLObject {
   //    NSString *_title;
}

//@synthesize title = _title;

- (instancetype)init
{
   self = [super init];
   if (self) {
       _title = @"微博@iOS程式犭袁";
   }
   return self;
}

- (NSString *)title {
   return _title;
}

- (void)setTitle:(NSString *)title {
   _title = [title copy];
}

@end

結果編譯器報錯: enter image description here

當你同時重寫了 setter 和 getter 時,系統就不會生成 ivar(例項變數/成員變數)。這時候有兩種選擇:

  1. 要麼如第14行:手動建立 ivar
  2. 要麼如第17行:使用@synthesize foo = _foo; ,關聯 @property 與 ivar。

更多資訊,請戳- 》 When should I use @synthesize explicitly?