一、@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