如何正確使用@synthesize(在有了自動合成屬性實例變量之後,@synthesize還有哪些使用場景?)

作者:代培
地址:http://blog.csdn.net/dp948080952/article/details/52611348
轉載請註明出處

寫在前面

這篇博客本來是回答《招聘一個靠譜的iOS》中在有了自動合成屬性實例變量之後,@synthesize還有哪些使用場景? 的這一問題,但是寫着寫着在瞭解了property的相關內容後,突然冒出了許多對@synthesize使用方法的理解,並且記錄了下來,希望分享給大家,與大家一同進步,如果理解的不正確,歡迎在評論區指正,有評論必回!

正文

所有場景

  1. 同時重寫了setter和getter函數
  2. 重寫了只讀屬性property的getter(這裏property必須聲明在.m文件中)
  3. 想要自定義實例變量的變量名
  4. 聲明在@protocol中的property
  5. 重載的屬性

最好不要在手動管理使用

從前兩點來看,當你準備自己手動管理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修飾的屬性,有三種方法:

  1. 直接修改property對應的ivar
  2. 重新在.m文件中聲明該屬性,並加上readwrite修飾
  3. 使用@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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章