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

iOS @property、@synthesize和@dynamic

@property

@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 @synthesize表示如果屬性沒有手動實現setter和getter方法,編譯器會自動加上這兩個方法。 @dynamic @dynamic 告訴編譯器:屬性的 setter 與 getter 方法由使用者自己實現,不自動生成。假如一個屬性被宣告為 @dynamic var,而且你沒有提供 @setter方法和 @getter 方法,編譯的時候沒問題,但是當程式執行到 instance.var = someVar,由於缺 setter 方法會導致程式崩潰;或者當執行到 someVar = var 時,由於缺 getter 方法同樣會導致崩潰。編譯時沒問題,執行時才執行相應的方法,這就是所謂的動態繫結。 例如:
#import <Foundation/Foundation.h> @interface People : NSObject @property (nonatomic, copy) NSString *name; @end #import "People.h" @implementation People @dynamic name; @end 當執行到: People *p= [People new]; p.name = @"Peter"; 程式就會crash,原因是“[People setName:]: unrecognized selector sent to instance”。 解決這種奔潰的方法有三種: 方法一: 最簡單粗暴,但是還是挺管用,直接註釋掉@dynamic name這行程式碼即可,由編譯器自動新增。 但是如果我們不想讓編譯器自動新增,那麼我們可以手動新增或則在執行時新增都可以。 方法二: 手動新增,由於@dynamic不能像@synthesize那樣向實現檔案(.m)提供例項變數,所以我們需要在類中顯式提供例項變數。
#import <Foundation/Foundation.h> @interface People : NSObject @property (nonatomic, copy) NSString *name; @end #import "People.h" @interface People () { NSString *_name; } @implementation People @dynamic name; - (void)setName:(NSString *)name { _name = [name copy]; } - (NSString *)name { return _name; } @end 方法三: 通過runtime機制在執行時新增屬性的存取方法。 在C函式中不能直接使用例項變數,需要將Objc物件self轉成C中的結構體,因此在Person類同樣需要顯式宣告例項變數而且訪問級別是@public #import <Foundation/Foundation.h> @interface People : NSObject @property (nonatomic, copy) NSString *name; @end #import "People.h" #import <objc/objc-runtime.h> @interface People () { @public NSString *_name; } @end @implementation People @dynamic name; + (BOOL)resolveInstanceMethod:(SEL)sel { if (sel == @selector(setName:)) { class_addMethod([self class], sel, (IMP)setName, "v@:@"); return YES; }else if (sel == @selector(name)){ class_addMethod([self class], sel, (IMP)getName, "@@:"); return YES; } return [super resolveInstanceMethod:sel]; } void setName(id self, SEL _cmd, NSString* name) { if (((People*)self)->_name != name) { ((People *)self)->_name = [name copy]; } } NSString* getName(id self, SEL _cmd) { return ((People *)self)->_name; } @end 作者:心至靜行至遠 連結:https://www.jianshu.com/p/c883687c6405 來源:簡書 著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。