synthesize & denamic

一、@property 本質

@property= ivar(實例變量) +getter/setter(存取方法);

在正規的 Objective-C 編碼風格中,存取方法有著嚴格的命名規范。 正因為有了這種嚴格的命名規范,所以 Objective-C 這門語言才能根據名稱自動創建出存取方法。

@property有兩個對應的詞,一個是 @synthesize,一個是 @dynamic。如果 @synthesize和 @dynamic都沒寫,那么默認的就是@syntheszie var = _var;


二、先來講講 “自動合成”(autosynthesis)

完成屬性定義后,編譯器會自動編寫訪問這些屬性所需的方法,此過程叫做“自動合成”(autosynthesis)

需要強調的是,這個過程由編譯器在編譯期執行,所以編輯器里看不到這些“合成方法”(synthesized method)的源代碼

例如

@interfacePerson:NSObject

@property NSString*firstName;

@property NSString*lastName;

@end

上述代碼寫出來的類與下面這種寫法等效:

@interfacePerson:NSObject

- (NSString*)firstName;

- (void)setFirstName:(NSString*)firstName;

- (NSString*)lastName;

- (void)setLastName:(NSString*)lastName;

@end

除了生成方法代碼 getter、setter 之外,編譯器還要自動向類中添加適當類型的實例變量,并且在屬性名前面加下劃線,以此作為實例變量的名字。

在前例中,會生成兩個實例變量,其名稱分別為 _firstName 與 _lastName。

也可以在類的實現代碼里通過 @synthesize 語法來指定實例變量的名字.

@implementationPerson

@synthesizefirstName = _myFirstName;

@synthesizelastName = _myLastName;

@end

我們每次在增加一個屬性,系統都會在 ivar_list 中添加一個成員變量的描述,在 method_list 中增加 setter 與 getter 方法的描述,在屬性列表中增加一個屬性的描述,然后計算該屬性在對象中的偏移量,然后給出 setter 與 getter 方法對應的實現,在 setter 方法中從偏移量的位置開始賦值,在 getter 方法中從偏移量開始取值,為了能夠讀取正確字節數,系統對象偏移量的指針類型進行了類型強轉.

三、@synthesize的作用

是如果你沒有手動實現 setter 方法和 getter 方法,那么編譯器會自動為你加上這兩個方法

如果 @synthesize和 @dynamic都沒寫,那么默認的就是@syntheszie var = _var;

四、什么情況下不會autosynthesis(自動合成)

同時重寫了 setter 和 getter 時

重寫了只讀屬性的 getter 時

使用了 @dynamic 時

在 @protocol 中定義的所有屬性

在 category 中定義的所有屬性

重載的屬性,當你在子類中重載了父類中的屬性,你必須使用 @synthesize 來手動合成ivar

五、@dynamic的作用

@dynamic 告訴編譯器:屬性的 setter 與 getter 方法由用戶自己實現,不自動生成。

當然對于 readonly 的屬性只需提供 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

@interface People : NSObject

@property (nonatomic, copy) NSString *name;

@end


#import "People.h"

#import <RACObjCRuntime.h>?

#import <RACEXTRuntimeExtensions.h>

@interface People ()

{

@public

? ? NSString*_name;

}

@end

@implementation People

@dynamic name;

+ (BOOL)resolveInstanceMethod:(SEL)sel {

? ? if(sel ==@selector(setName:)) {

? ? ? ? class_addMethod([selfclass], sel, (IMP)setName,"v@:@");

? ? ? ? returnYES;

? ? }elseif(sel ==@selector(name)){

? ? ? ? class_addMethod([selfclass], sel, (IMP)getName,"@@:");

? ? ? ? returnYES;

? ? }

? ? return [super resolveInstanceMethod:sel];

}

void setName(idself,SEL_cmd,NSString* name)

{

? ? if(((People*)self)->_name!= name) {

? ? ? ? ((People*)self)->_name= [namecopy];

? ? }

}

NSString* getName(idself,SEL_cmd)

{

? ? return((People*)self)->_name;

}

@end

參考原文鏈接:http://www.lxweimin.com/p/c883687c6405

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。