@property、@synthesize 、@dynamic的應用
阿新 • • 發佈:2020-08-19
@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方法。 另外需要注意的是,子類同名屬性如果和父類的屬性型別不同。則可能會崩潰。所以不要這樣寫,換個變數名字好了。