1. 程式人生 > 實用技巧 >@property、@synthesize 、@dynamic的應用

@property、@synthesize 、@dynamic的應用

@property的本質
@property = ivar(下劃線例項變數) + getter/setter(存取方法);

在正規的 Objective-C 編碼風格中,存取方法有著嚴格的命名規範。 正因為有了這種嚴格的命名規範,所以 Objective-C 這門語言才能根據名稱自動創建出存取方法。 完成屬性定義後,編譯器會自動編寫訪問這些屬性所需的方法,此過程叫做“自動合成”(autosynthesis)。需要強調的是,這個過程由編譯 器在編譯期執行,所以編輯器裡看不到這些“合成方法”(synthesized method)的原始碼。除了生成方法程式碼 getter、setter 之外,編譯器還要自動向類中新增適當型別的例項變數,並且在屬性名前面加下劃線,以此作為例項變數的名字。 我們每次在增加一個屬性,系統都會在 ivar_list 中新增一個成員變數的描述,在 method_list 中增加 setter 與 getter 方法的描述,在屬性列表中增加一個屬性的描述,然後計算該屬性在物件中的偏移量,然後給出 setter 與 getter 方法對應的實現,在 setter 方法中從偏移量的位置開始賦值,在 getter 方法中從偏移量開始取值,為了能夠讀取正確位元組數,系統物件偏移量的指標型別進行了型別強轉. @property有兩個對應的詞,一個是 @synthesize,一個是 @dynamic。如果 @synthesize和 @dynamic都沒寫,那麼預設的就是@syntheszie var = _var; (自動合成的)

自動合成(@synthesize)

預設就是自動合成

動態合成(@dynamic)

既然有自動合成,那麼相對應的就要有非自動合成,非自動合成又稱為動態合成。 1. 什麼是synthesize synthesize中文意思是合成,程式碼中我們經常這樣用。
1 @interface Test: NSObject
2 @property (nonatomic, unsafe_unretained) int i;
3 @end
4  
5 @implementation Test
6 @synthesize i;
7 @end

使用synthesize的2個步驟:
  • 首先你要有在類宣告中使用property宣告的屬性。
  • 第二在類實現中,寫出 @synthesize + 變數名。

2. synthesize有什麼作用?

平時在使用中,在類宣告中添加了property後,根本不需要在實現中新增@synthesize。 因為OC為property屬性宣告添加了自動合成,也就是說系統自動幫你添加了@synthesize。 所以,synthesize是property屬性的一部分,它是系統為property生成變數的重要步驟。 synthesize具體做了些什麼呢?它只做2件事:
  • 生成成員變數,如上面的Test類就會生成一個名字為 _i 的 int型變數。
  • 為屬性生成setter/getter方法,如上面的Test類會生成setI:和i兩個方法。

3. synthesize什麼情況下會用?

正常情況下,你不需要使用的synthesize,因為大多數情況下,系統都會為你自動合成。 但是,你必須能清楚,系統自動合成有時候也是會失效的。這時候就需要你手動新增 synthesize。 這些情況大約有3種:
  • 修改生成的成員變數名字。
  • 手動添加了 setter/getter 方法。
  • 實現了帶有 property 屬性的 protocol。

3.1 修改生成的成員變數名字

 1 @interface Test: NSObject
 2 @property (nonatomic, readonly, unsafe_unretained) int i;
 3 @end
 4  
 5 @implementation Test
 6  
 7 @synthesize i = shuaiI;
 8  
 9 //@synthesize i; //如果只寫這個相當於 @synthesize i = i;
10  
11 -(void) print{
12     //NSLog(@" print test i = %d", _i); //這個_i好土,換個帥氣的名字。
13     NSLog(@" print test i = %d", shuaiI); //帥氣的名字就是shuaiI。
14 }
15 @end

雖然可以使用@synthesize關鍵字修改變數名,但是如無特殊需求,不建議這樣做。因為預設情況下編譯器已經為我們生成了變數名,大多數的專案、開發者也都會遵循這樣的規範,既然蘋果已經定義了一個好的規範,為什麼不遵守呢?

3.2 手動添加了setter/getter方法

如果你的屬性是隻讀屬性,但是你重寫了getter方法,系統不會為你自動生成成員變數。你需要新增@synthesize。
 1 @interface Test: NSObject
 2 @property (nonatomic, readonly, unsafe_unretained) int i;
 3 @end
 4  
 5 @implementation Test
 6 -(int)i{
 7     return _i;
 8 }
 9  
10 @synthesize i = _i;//不加這個會報錯
11  
12 -(void) print{
13     NSLog(@" print test i = %d", _i);
14 }
15  
16 @end

如果你的屬性可讀可寫,但是你同時重寫了setter/getter方法,系統不會為你自動生成成員變數。你需要新增@synthesize。這種情況下,你如果只重寫了setter/getter其中一個,系統仍然會執行自動合成。
 1 @interface Test: NSObject
 2 @property (nonatomic, unsafe_unretained) int i;
 3 @end
 4  
 5 @implementation Test
 6  
 7 -(int)i{
 8     return _i;
 9 }
10  
11 -(void) setI:(int)i{
12     _i = i;
13 }
14  
15 @synthesize i = _i;//不加這個會報錯
16  
17 -(void) print{
18     NSLog(@" print test i = %d", _i);
19 }
20  
21 @end

3.3 實現了帶有property屬性的protocol

 1 @protocol TestProtocol<NSObject>
 2 @property (nonatomic, unsafe_unretained) int i;
 3 @end
 4  
 5 @interface Test: NSObject<TestProtocol>
 6 @end
 7  
 8 @implementation Test
 9  
10 @synthesize i = _i;//不加這個會報錯
11  
12 -(void) print{
13     NSLog(@" print test i = %d", _i);
14 }
15  
16 @end

4. 其他不會自動合成的情況

還有些情況,系統不會為屬性自動合成變數和setter/getter方法,但是你也不需要手動新增@synthesize。 這些情況有:
  • 使用了@dynamic。
  • 在Category中新增的property。
  • 子類覆蓋了父類的同名屬性。

4.1 什麼是@dynamic

@dynamic 使用場合同 @synthesize,它的作用和@synthesize相反,它告訴系統,不要為property宣告的屬性新增成員變數。會有其他地方新增的。 所以用到@dynamic的地方很少,那麼在什麼情況下會使用到呢? 能想到的大概只有1種:動態生成類和變數的情景中。如動態model生成。

4.2 Category中宣告property

Category中直接宣告property是沒有意義的,相當於沒寫。 所以你在Category中即使寫了property,也要手動新增setter/getter方法。 property就是為了簡寫setter/getter方法,你都手動寫了,也就沒必要加property了。 但是你仍然可以寫property。

4.3 子類覆蓋父類同名屬性

 1 @interface Super: NSObject
 2 @property (nonatomic, unsafe_unretained) int i;
 3 @end
 4  
 5 @implementation Super
 6 @end
 7  
 8 @interface Child: Super
 9 @property (nonatomic, unsafe_unretained) int i;
10 @end
11  
12 @implementation Child
13  
14 -(int)i{
15 return 1;
16 }
17  
18 +(void) test{
19 IMP superSetterMethod = class_getMethodImplementation(Super.class, @selector(setI:));
20 IMP childSetterMethod = class_getMethodImplementation(Child.class, @selector(setI:));
21 NSLog(@" setter is Equal = %d", superSetterMethod == childSetterMethod);
22 IMP superGetterMethod = class_getMethodImplementation(Super.class, @selector(i));
23 IMP childGetterMethod = class_getMethodImplementation(Child.class, @selector(i));
24 NSLog(@" getter is Equal = %d", superGetterMethod == childGetterMethod);
25 }
26 @end

上面程式碼寫出來,xcode就會有提示,告訴你要為Child的i屬性新增@synthesize。 而執行[Child test]的輸出結果為: 1 0 複製程式碼 說明Child裡雖然寫了property,但是並未生成setter/getter方法。 另外需要注意的是,子類同名屬性如果和父類的屬性型別不同。則可能會崩潰。所以不要這樣寫,換個變數名字好了。