OC 中 覆蓋父類屬性會有Auto property synthesis will not synthesize property 'xxx'的警告

情景還原
今天無意間弄了一個警告. 警告就會存在著一定的隱藏風險,所以就一定要解決這種異常風險,接下來我們看一下當時的情景程式碼.
我首先寫了一個名為 BaseObject 的基類 ,基類中具有一個遵循BaseObjectDelegate協議的代理屬性物件delegate,如下所示
#import <Foundation/Foundation.h> @protocol BaseObjectDelegate<NSObject> - (void)baseObjectDeleagateAction; @end @interface BaseObject : NSObject @property(nonatomic,weak)id <BaseObjectDelegate>delegate; @end
然後,我寫了一個繼承於 BaseObject 的子類 SubObject , SubObject 中具有一個遵循SubObjectDelegate協議的代理屬性物件delegate,程式碼如下所示.
#import "BaseObject.h" @protocol SubObjectDelegate<BaseObjectDelegate,NSObject> - (void)subObjectDeleagateAction; @end @interface SubObject : BaseObject @property(nonatomic,weak)id <SubObjectDelegate>delegate; @end
這時候,子類 SubObject 就會報警告.截圖如下所示.

文字錯誤:Auto property synthesis will not synthesize property 'delegate'; it will be implemented by its superclass, use @dynamic to acknowledge intention
這種情況就類似於 UIScollView 和子類 UITableView 的關係,兩者都還有遵循不同協議的delegate屬性,但是並沒有警告產生.所以這個警告我們要好好研究一下.
解決方案
我們先說說怎麼來解決這個警告,然後我們再來聊一下這個警告是如何產生的以及有什麼安全隱患.
首先,我們不會去遮蔽這種警告,那麼怎麼辦呢?最簡單的方式就是把子類的delegate 改成別的名稱,例如 subDelegate ,這樣子類就會有兩個代理屬性,一個是繼承於父類的,一個是自己定義的.程式碼如下所示.
@interface SubObject : BaseObject @property(nonatomic,weak)id <SubObjectDelegate>subDelegate; @end
還有一種解決方案就是使用 @synthesize 關鍵詞.我們在.m檔案中編寫如下程式碼即可解決警告.
#import "SubObject.h" @implementation SubObject @synthesize delegate = _delegate; @end
警告分析
下面我們就來分析一下警告產生過程以及警告會有什麼樣的安全隱患.我查閱了網上了很多前人所寫的部落格,這裡只是做了一下記錄,並非自己驗證過的結果.所以要注意.
要了解這個警告,我們就要了解什麼是屬性,在OC中屬性的定義就是給一個類的成員變數提供了封裝,關鍵字是 @property .
Objective-C properties offer a way to define the information that a class is intended to encapsulate。
通過宣告屬性,我們可以很簡單的為一個成員變數定義其是否是隻讀的還是讀寫的,是否是原子操作的等等特性,也就是說如果說封裝是為成員變數套了一層殼的話,那麼 @property關鍵字做的事情就是預定義這層殼是個什麼樣子的殼,然後通過 @sythesize關鍵字生成真正的殼並把這個殼套在實際的成員變數上(如果沒有定義這個成員變數該關鍵字也可以自動生成對應的成員變數)。當然這層殼包括了自動生成的get set 方法。
但是好像現在的開發人員對 @sythesize 的使用頻率很低了,那麼 @sythesize 到底有什麼作用呢?在最開始的時候,我們在程式碼中寫了 @property 對應的就要寫一個 @sythesize ,在蘋果使用了LLVM (2005年Swift之父Chris Lattner將蘋果使用的 GCC 全面轉為 LLVM)作為編譯器以後,如果我們沒有寫 @sythesize ,編譯器就會為我們自動的生成一個 @sythesize property = _property。這個特性叫做 Auto property synthesize 。
說了這麼多,現在我們來回頭看看問題的關鍵,當我們想覆蓋父類的屬性並做一些修改的時候,Auto property synthesize這個特性就有點不知道該幹嘛了,這個時候他選擇不跑出來為我們幹活,所以編譯器就不會自動生成@sythesize property = _property,但是子類總得有個殼啊,人家都有@property了,怎麼辦?直接拿過來父類的殼複製一份不管三七二十一套在子類的成員變數身上。注意,有些情況下這會產生執行時的crash,比如:
一個父類 A
@interface A : NSObject @property(strong,nonatomic,readonly)NSString *name; @end
子類Aa
@interface Aa : A @property(strong,nonatomic,readwrite)NSString *name; @end
這種情況下編譯器會給出 warning:
Auto property synthesis will not synthesize property 'name' because it is 'readwrite' but it will be synthesized 'readonly' via another property
注意,雖然只給出了 warning,但是這個時候顯然 Aa 中是不會自動生成 set 方法的,如果在程式碼中呼叫了 Aa 的例項物件的 set 方法,執行時就會 crash,crash 原因是:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Aa setName:]: unrecognized selector sent to instance
所以遇到這個問題怎麼解決?在子類中顯式的宣告一個@synthesize name = _name;就好,這樣子類就會如願的產生他的殼,編譯器也不糾結了,就去掉了 warning,這樣問題就解決了.
問題總結
這個問題歸根到底是因為 Auto property synthesize 不工作,導致子類屬性直接把父類屬性的修飾屬性複製到自己身上,這樣就會產生安全隱患,最終生成警告. 所以我們要自己寫一個顯式的宣告 @synthesize name = _name; 即可解決問題.
ofollow,noindex">參考原文連結
