作者:代培
地址:http://blog.csdn.net/dp948080952/article/details/52611348
轉載請註明出處
寫在前面
這篇博客本來是回答《招聘一個靠譜的iOS》中在有了自動合成屬性實例變量之後,@synthesize還有哪些使用場景? 的這一問題,但是寫着寫着在瞭解了property的相關內容後,突然冒出了許多對@synthesize使用方法的理解,並且記錄了下來,希望分享給大家,與大家一同進步,如果理解的不正確,歡迎在評論區指正,有評論必回!
正文
所有場景
- 同時重寫了setter和getter函數
- 重寫了只讀屬性property的getter(這裏property必須聲明在.m文件中)
- 想要自定義實例變量的變量名
- 聲明在@protocol中的property
- 重載的屬性
最好不要在手動管理使用
從前兩點來看,當你準備自己手動管理property的所有內容時,編譯器就不會幫你自動生成,此時就需要用到@synthesize手動生成(需要注意的是@synthesize必須要寫在@implementation中)。
其實對於前兩種情況,我覺得是不適合使用@synthesize的,爲什麼呢?首先要看@synthesize是做什麼的,他是幫你自動實現ivar和訪問方法,而你已經完全重寫了訪問方法,此時@synthesize只是讓他幫你實現了一個ivar,已經背離了它本身該做的事情,爲何不直接自己聲明一個ivar呢,同樣只要一行代碼,這樣卻更加自然一些。
所以分析到最後,如果想要完全接管@property,最好不要用@synthesize,但是當然你是可以使用@synthesize的,所以這也是@synthesize的一種情況,在這種情況下,@synthesize只有一個作用就是幫你生成ivar。
而且與@synthesize相對的@dynamic,就是讓你動態的實現一個屬性的訪問方法和ivar,如果你想要完全接管其property,最好用@dynamic修飾property,雖然不是必要的,但是顯示的聲明可以讓別人更容易看懂你的代碼:你是自己實現的這個屬性。
不推薦用來自定義變量名
@synthesize還可以用來自定義Property所對應的ivar的名稱
假設有一個屬性firstName
@synthesize firstName;
上述代碼就會生成一個firstName的實例變量與firstName屬性相對應。
@synthesize firstName = myFirstName;
該種方式則可以自定義Property所對應的實例變量名稱
當然這種方式並不推薦,因爲如果大家可能更多使用的是默認的方案,這樣所有人寫出的代碼都更容易理解。
最好的用法:在protocol中聲明屬性
在protocol聲明property,讓實現這個協議的類共享一些屬性,就像協議定義方法一樣,目的是隻提供一個統一接口的規範,而不給出具體的實現,屬性也是相同,屬性的實例變量也需要類來自己synthesize,而且編譯器不會自動幫你生成,需要注意的是最好不要將Property放在@optional中,因爲這樣當你沒有自己合成ivar時Xcode不會給你警告,而當你在訪問這些屬性時不會有問題,而當運行時會報 unrecognized selector sent to instance的異常,程序會崩潰。
有兩種方式實現protocol中的屬性,第一個是使用@synthesize,一句話就可以自動生成ivar和訪問方法(當然你也可以重寫setter和getter方法)。第二種方法是完全接手,自己聲明實例變量(ivar的名稱完全可以自己定義,他和屬性的關聯實際上是在訪問方法中關聯的),同時實現setter和getter方法(兩個方法少一個編譯器都會有警告)。總的來說兩種方法都可以自定義實例變量名,第一種方法更簡便且更清晰,同時只要重寫自己需要的定製的訪問方法,所以更推薦第一種方法。
重載屬性時最好使用@dynamic
這是我在Xcode中重載一個屬性時編譯器給的警告,警告中很明顯的告訴我們這種情況下應該使用@dynamic
Auto property synthesis will not synthesize property ‘xxx’; it will be implemented by its superclass, use @dynamic to acknowledge intention
那到底能不能用@synthesize呢?我們就試試唄
@interface ViewController : UIViewController
@property(nullable, nonatomic, readonly, copy) NSString *nibName;
@end
@implementation ViewController
@synthesize nibName = myNibName;
- (void)viewDidLoad {
[super viewDidLoad];
self.nibName = @"DaiPei";
NSLog(@"the ivar myNibName:%@", myNibName);
}
- (void)setNibName:(NSString * _Nullable)nibName {
myNibName = nibName;
}
- (NSString *)nibName {
return myNibName;
}
@end
新建一個工程,重載ViewController的一個叫nibName的屬性(本來重載了其view屬性,發現這個vc完全沒有用了,連viewDidLoad方法都進不去,然後就挑了一個看似不是很重要的屬性)
然後最後打印的結果是the ivar myNibName:DaiPei
,說明重載成功了
那如果把setter和getter方法刪去呢?結果是相同的
所以結論是@synthesize可以在重載時使用的,但是我覺得最好的方式還是使用@dynamic,畢竟這是蘋果官方推薦的,@synthesize在這裏總感覺不是那麼優雅!
@synthesize可以強行改變readonly性質
有一個比較有趣的現象,在頭文件中聲明的只讀property,可以重寫其getter方法,而不需要使用@synthesize去生成實例變量,但如果是在.m中聲明的property,重寫其getter方法時,編譯器就不會幫我們自動生成實例變量了。
雖然說.h文件是暴露給外面的一個文件,但在裏面聲明的readonly屬性在類的內部也是無法修改的
如何在內部修改.h中有readonly修飾的屬性,有三種方法:
- 直接修改property對應的ivar
- 重新在.m文件中聲明該屬性,並加上readwrite修飾
- 使用@synthesize,同時生成getter和setter方法(等於是強行使property屬性變成了readwrite,我發現即使是在.m文件聲明的屬性也是可以的)
當然這種我這裏只是提出@synthesize的這個性質,我並不推薦這種用法,我卻覺得這種用法不好,這並不是@synthesize本應該做的事情,如果需要讓一個屬性在內外有不同表現,第二種方法應該是最好的,顯示的表明這個屬性的在內外有不同的表現。
勘誤
- 在category中聲明屬性需要使用@synthesize
有些地方說@synthesize可以用在category中屬性的實例變量的合成,我試了一下是不行的,會報錯:@synthesize not allowed in a category’s implementation,我不知道以前是不是可以,我的環境是Xcode7,編譯器版本:Apple LLVM version 7.3.0 (clang-703.0.29),在category中添加屬性只能使用Associated Object來實現,還有一種不優雅的實現方式是用一個單例來保存這些實例遍歷。
- 使用@dynamic時需要使用@synthesize
實際上@dynamic與@synthesize是相對的,也是用來修飾property的,表示實例變量的setter和getter方法手工實現或是在運行時生成。
總結
如果沒有ivar的支持,屬性單獨是沒有太多作用的,屬性本身的作用我想是在於對實例變量在原子性、讀寫權限、內存管理、訪問方法進行封裝,並且聲明(注意這裏是聲明)setter和getter方法(即使你沒有實現這兩個方法,都在代碼裏都可以調用這兩個方法)。
當你在頭文件或是類擴展裏聲明一個屬性時,編譯器會在編譯期間自動插入一句@synthesize yourProperty = yourProperty;(Objective-C Autosynthesis of Properties,這是clang的支持的特性,實際上自動生成和手動生成效果是相同的)幫你生成一個以開頭的同名的實例變量(需要注意的是,前面這句話是不對的,是我最開始的理解,後來我發現自動生成和手動生成有一點不一樣,就是自動生成會根據你屬性中的修飾詞選擇性的生成getter和setter方法,而@synthesize沒有那麼智能,會同時生成setter和getter方法),這句代碼同時會按照你聲明屬性時給出的修飾符自動生成其setter、getter方法,需要注意的是@synthesize做的事情不僅是生成實例變量,還也會生成getter和setter的實現。
所以何時要使用@synthesize?其實很簡單,就是在某些編譯器無法自動幫你加上這句話的場景下,你自己不想親自聲明ivar實現訪問方法時,使用此保留字幫你快捷實現。而實際上這個情況只有一個對於聲明在protocol中的property。