Runtime--Property

Property

Objective-C中阎曹,接口的聲明和實現(xiàn)分開在兩個文件中:.h文件問聲明文件虎谢,.m文件為實現(xiàn)文件今妄。

//.h文件中
@interface Person : NSObject
 //在此聲明接口
@end

//.m文件中
#import "Person.h"
 
@implementation Person
 //在此實現(xiàn)接口
@end

對象一般通過properties來封裝數(shù)據(jù)并提供公共訪問团滥,例如

@interface Person : NSObject
 
@property NSString *firstName;
@property NSString *lastName;
 
@end

Person有兩個properties可以供外界訪問昏兆。Objective-C中property可以聲明attributes來修飾property枫虏,比如說讀寫權(quán)限、原子訪問爬虱、強弱引用等隶债。格式:@property+(attributes,attributes...)+property,其中@property為關(guān)鍵字固定不變饮潦,(attributes)括號固定不變燃异,修飾詞多個用逗號隔開,property為訪問名稱继蜡。例如

@interface Person : NSObject
@property (readonly) NSString *firstName;
@property (readonly,copy) NSString *lastName;
@end

@property本質(zhì)

當你使用 @property關(guān)鍵字來聲明一個property時回俐,編譯器會自動生成器對象的訪問方法:set方法和get方法。規(guī)則如下:
1稀并、get方法名稱同property一樣仅颇,例如firstName對應(yīng)firstName
2、set方法名稱:set前綴+property名稱首字母大寫碘举,例如firstName對應(yīng)setFirstName:
注意:如果property聲明為readonly忘瓦,編譯器將不生成set方法

@property自定義訪問方法

如果需要自定義訪問訪問,可以property聲明的時候引颈,在attributes中通過setter和getter關(guān)鍵字來指定方法名耕皮,此時編譯器值自動生成指定的方法名境蜕。例如

@property (nonatomic,assign,setter=isSetHave:,getter=isHave)BOOL have;

@property與點語法

@property聲明的property編譯器自動生成對應(yīng)的訪問方法,通過訪問方法可以訪問凌停;同時通過點語法(俗稱打點調(diào)用)也可以訪問粱年。例如

//People生成對象somePerson
//1、get
[somePerson firstName];
somePerson.firstName;

//2罚拟、set
[somePerson setFirstName:@"Johnny"];
somePerson.firstName = @"Johnny";

@property與Instance Variables

Instance Variables(以下稱實例變量)指在一個對象生存時存在并且保存值的變量台诗,其內(nèi)存申請和釋放和對象的創(chuàng)建 (alloc)和銷毀(dealloc)同步進行。

一般情況下赐俗,可以讀寫的property的值被實例變量保存拉队,該實例變量有編譯器自動生成,若無特殊聲明阻逮,其名稱:下劃線“_” + property粱快,例如firstName對應(yīng)_firstName 。

一般來說建議通過訪問方法和點語法訪問properties夺鲜。然而在.m文件中皆尔,還可以通過property來訪問,而且符合可以清晰的區(qū)別局部變量和實例變量币励。例如

- (void)someMethod 
  {
   //局部變量
    NSString *firstName = @"An interesting string";
    //實例變量
    _firstName = firstName;
  }

而且在initialization(初始化)慷蠕、 deallocation(銷毀)、 custom accessor methods(訪問方法)方法中建議通過實例變量直接訪問property食呻。例如

- (instancetype)init
{
    self = [super init];
    if (self) {
        _firstName = @"zwq";
    }
    return self;
}

-(void)setFirstName:(NSString *)firstName
{
    _firstName = firstName;
}

自定義實例變量名稱

如果想自定義實例變量名稱流炕,可以通過以下語法來實現(xiàn)

//原來_propertyName

@implementation YourClass
@synthesize propertyName = instanceVariableName;
...
@end

例如

@synthesize firstName = ivar_firstName;

其中property的名稱還是firstName,set和get方法仅胞、點語法均可以正常訪問每辟,但是實例變量名稱已經(jīng)變成ivar_firstName,以后通過此名稱直接訪問干旧;例如

@synthesize firstName = _myFirstName;


-(void)setFirstName:(NSString *)firstName
{
    _firstName = firstName;
}

改成

-(void)setFirstName:(NSString *)firstName
{
    _myFirstName = firstName;
}

@synthesize firstName;如果這么寫渠欺,默認實例變量名修改為firstName不再有下劃線。

其實還可以通過以下代碼獲取指定類的實例變量椎眯,對比前后打印結(jié)果即可驗證上述結(jié)論

#import <objc/runtime.h>

/* 獲取變量列表 */
unsigned int count = 0;
Ivar *list =  class_copyIvarList([Data class], &count);
for (int i = 0; i < count ; i ++)
{
   Ivar var = list[i];
   const char *name_var = ivar_getName(var);
   NSLog(@"Ivar:%@",[NSString stringWithUTF8String:name_var]);
}
free(list);

注意

以下情況編譯器不會自動生成實例變量:
1挠将、對于可讀寫的property,當你自己同時實現(xiàn)set和get方法時
2编整、對于只讀的property舔稀,當你實現(xiàn)get方法時
以上兩種情況,編寫代碼時編譯器會提示你未定義變量掌测,此時需要自己定義一個實例變量@synthesize property = _property;内贮,以firstName為例

/****.h文件****/
@interface People : NSObject

@property (nonatomic,copy)NSString  *firstName;

@end

/****.m文件****/
@implementation People
@synthesize firstName = _firstName;//自定義實例變量名稱

-(void)setFirstName:(NSString *)firstName
{
    _firstName = firstName;
}

-(NSString*)firstName
{
    return _firstName;
}
@end

@property與原子性

Objective-C 的property默認原子訪問。
這就意味著訪問方法在訪問值期間,獨立持有該property的value夜郁,即使多線程訪問也是如此什燕。
由于原子方法的同步機制的實現(xiàn)是私有的,所以同步自定義訪問方法和編譯器自動實現(xiàn)的方法行不通的拂酣。例如對于一個readwrite的property秋冰,自己實現(xiàn)set方法,編譯器實現(xiàn)的get方法婶熬,此二者的組合實現(xiàn)不能實現(xiàn)原子性訪問,而且編譯器給出警告提示:

@property (copy)NSString  *firstName;

-(NSString*)firstName
{
    return _firstName;
}

//??提示語埃撵,并給出修改建議
Writable atomic property 'firstName' cannot pair a synthesized setter with a user defined getter

Setter and getter must both be synthesized, or both be user defined,or the property must be nonatomic

Property declared here

原子的關(guān)鍵字atomic(默認實現(xiàn)赵颅,無需指定)和非原子性nonatomic。如果五原子性要求暂刘,使用nonatomic可以提示性能饺谬、訪問速度。

@property (nonatomic)NSString  *firstName;
@property (atomic)NSString  *firstName;

原子性并不意味著線程安全

例如姓名=姓氏+名字谣拣;批量修改一系列名字
線程A:1募寨、修改姓氏 2、修改名字
線程B:3森缠、獲取姓名

@property (copy)NSString  *firstName;//姓氏
@property (copy)NSString  *secondName;//名字
@property (copy)NSString  *fullName;//姓名

雖然姓氏和名字都是原子訪問拔鹰,當3在1和2直接進行的話,是不能保證獲取到是同一個人的姓名贵涵。

@property與其它對象

注意循環(huán)引用

如果property是其它對象的列肢,注意避開循環(huán)引用。經(jīng)典的例子請參考UITableView的delegates的設(shè)置宾茂。

@property與copy

經(jīng)典的例子是聲明NSSting類型時瓷马,建議使用copy。通過下變例子代碼說明

@interface People : NSObject

@property (nonatomic,strong)NSString  *firstName;

@end

NSMutableString *firstName = [[NSMutableString alloc] initWithString:@"張"];
People *data = [[People alloc] init];
data.firstName = firstName;
[firstName appendString:@"三"];
NSLog(@"%@--firstName:%p--tempStr:%p",data.firstName,data.firstName,tempStr);

//輸出結(jié)果
 張三--firstName:0x7fd81a534250--tempStr:0x7fd81a534250
@interface People : NSObject

@property (nonatomic,copy)NSString  *firstName;

@end

NSMutableString *firstName = [[NSMutableString alloc] initWithString:@"張"];
People *data = [[People alloc] init];
data.firstName = firstName;
[firstName appendString:@"三"];
NSLog(@"%@--firstName:%p--tempStr:%p",data.firstName,data.firstName,tempStr);

//輸出結(jié)果
 張--firstName:0x7fd4a940f200--tempStr:0x7fd4a945fab0

對比以上兩段代碼的輸出結(jié)果不難發(fā)現(xiàn)strong和copy的區(qū)別:前者直接引用一份跨晴,后者拷貝一份欧聘。根據(jù)需要選擇使用。

Runtime-Property

Property在Runtime中被如此定義
objc_property_t

/// An opaque type that represents an Objective-C declared property.
typedef struct objc_property *objc_property_t;

可以獲取的屬性有

//獲取name
const char *property_getName(objc_property_t property)

//獲取所有屬性端盆,以C字符串返回
const char *property_getAttributes(objc_property_t property) 

//獲取所有屬性怀骤,以數(shù)組返回; 數(shù)組需要free
objc_property_attribute_t *property_copyAttributeList(objc_property_t property, unsigned int *outCount)

//獲取指定屬性值爱谁;
char * property_copyAttributeValue(objc_property_t property, const char *attributeName);

和其相關(guān)的objc_property_attribute_t

/// Defines a property attribute
typedef struct {
    const char *name;           /**< The name of the attribute */
    const char *value;          /**< The value of the attribute (usually empty) */
} objc_property_attribute_t;

對于一個類來說晒喷,runtime對于property的操作分3種:增、改访敌、查

//增
class_addProperty
//改
class_replaceProperty
//查
class_getProperty
class_copyPropertyList

先從查說起凉敲。首先是定義一個簡單的類

@interface Data : NSObject

@property (nonatomic,copy)NSString  *firstName;

@end

獲取指定的Property

//獲取Data的firstName的property
objc_property_t property_firstName = class_getProperty([Data class], "firstName");
NSLog(@">>%@",[NSValue value:&property_firstName withObjCType:@encode(objc_property_t)]);

獲取Property列表

//Property列表
unsigned int property_count = 0;
objc_property_t *property_list =  class_copyPropertyList([Data class], &property_count);
    
for (int i = 0; i < property_count ; i ++)
{
   objc_property_t property = property_list[i];
   const char *name_property = property_getName(property);//名稱
   const char *name_attributes = property_getAttributes(property);//屬性字符串
   NSLog(@"property:%@  attributes:%@",[NSString stringWithUTF8String:name_property],[NSString stringWithUTF8String:name_attributes]);

}
    
free(property_list);//必須free

//輸出
property:firstName  attributes:T@"NSString",C,N,V_firstName

獲取Name

const char *name_property = property_getName(property_firstName);
NSLog(@"property:%@",[NSString stringWithUTF8String:name_property]);
//輸出
property:firstName

獲取所有屬性,以C字符串返回

const char *name_attributes = property_getAttributes(property_firstName);
NSLog(@"attributes:%@",[NSString stringWithUTF8String:name_attributes]);
//輸出
attributes:T@"NSString",C,N,V_firstName
關(guān)于Attributes

例如T@"NSString",C,N,V_firstName的解釋說明,通過property_getAttributes函數(shù)獲取Property所有屬性爷抓,以C字符串返回势决,格式如下:
T + @encode type + , +...(其它屬性逗號隔開)+,V+backing instance variable(Property對應(yīng)的屬性變量)
其中@encode type如圖所示

14741669452582.jpg

其中Attributes類型如下

14741670735981.jpg

其中backing instance variable說明見第一部分。

獲取所有屬性蓝撇,以數(shù)組返回

//屬性列表
unsigned int attribute_count = 0;
objc_property_attribute_t *attribute_list = property_copyAttributeList(property_firstName, &attribute_count);
for (int x = 0; x < attribute_count; x ++)
{
   objc_property_attribute_t attribute = attribute_list[x];
   const char *name_attributes = property_copyAttributeValue(property_firstName, attribute.name);//同結(jié)構(gòu)體直接取值
   NSLog(@"attributes:[%@:%@]",[NSString stringWithUTF8String:attribute.name],[NSString stringWithUTF8String:attribute.value]);
   
}    
free(attribute_list);//必須free

//輸出
attributes:[T:@"NSString"]
attributes:[C:]
attributes:[N:]
attributes:[V:_firstName]

獲取指定屬性值

//獲取指定屬性值
const char *attribute_T = property_copyAttributeValue(property_firstName, "T");
NSLog(@"attribute_T:%@",[NSString stringWithUTF8String:attribute_T]);
const char *attribute_V = property_copyAttributeValue(property_firstName, "V");
NSLog(@"attribute_V:%@",[NSString stringWithUTF8String:attribute_V]);

//輸出
attribute_T:@"NSString"
attribute_V:_firstName

/*
 * @param cls 修改的類
 * @param name 添加的名稱
 * @param attributes An array of property attributes.
 * @param attributeCount Attributes數(shù)量(寫少了從前向后裙础)
 * @return 成功YES,否則NO(例如已存在)
 * /
BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)

示例代碼如下

/* property  添加:添加失敗返回NO渤昌,例如屬性已存在 */
objc_property_attribute_t attr_T = {"T","@\"NSString\""};//@encod
objc_property_attribute_t attr_N = {"N",""};//原子性
objc_property_attribute_t attr_C = {"C",""};//copy
objc_property_attribute_t attr_V = {"V","_secondName"};//實例變量
    
objc_property_attribute_t attrs[] = {attr_T,attr_N,attr_C,attr_V};
BOOL isAdd = class_addProperty([Data class], "secondName", attrs, 4);
NSLog(@"添加結(jié)果:%d",isAdd);

/* 驗證是否修改成功 */
unsigned int count = 0;
objc_property_t *list =  class_copyPropertyList([Data class], &count);
for (int i = 0; i < count ; i ++)
{
   objc_property_t property = list[i];
   const char *name_property = property_getName(property);//名稱
   const char *name_attributes = property_getAttributes(property);//屬性字符串
   NSLog(@"property:%@  attributes:%@",[NSString stringWithUTF8String:name_property],[NSString stringWithUTF8String:name_attributes]);
   
}
free(list);

//輸出
添加結(jié)果:1
property:secondName  attributes:T@"NSString",N,C,V_secondName
property:firstName  attributes:T@"NSString",C,N,V_firstName

/** 
 * Replace a property of a class. 
 * 
 * @param cls 修改的類.
 * @param name 修改的property名稱.
 * @param attributes An array of property attributes.
 * @param attributeCount Attributes數(shù)量(寫少了從前向后人涑)
 * @注意 如果替換property不存在則執(zhí)行add操作
 */
void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)

示例代碼

/* property  添加:添加失敗返回NO,例如屬性已存在 */
objc_property_attribute_t attr_T = {"T","@\"NSString\""};//@encod
objc_property_attribute_t attr_N = {"N",""};//原子性
objc_property_attribute_t attr_C = {"C",""};//copy
objc_property_attribute_t attr_V = {"V","_secondName"};//實例變量
    
objc_property_attribute_t attrs[] = {attr_T,attr_N,attr_C,attr_V};
BOOL isAdd = class_addProperty([Data class], "secondName", attrs, 4);
NSLog(@"添加結(jié)果:%d",isAdd);
    
/* 替換*/
//若不存在独柑,則執(zhí)行add操作生成新的Property
class_replaceProperty([Data class], "number_replace", attrs, 4);
    
//替換:去除了原子性和copy
objc_property_attribute_t attrs_replace[] = {attr_T,attr_V};
class_replaceProperty([Data class], "secondName", attrs_replace, 2);
    
/* 驗證是否修改成功 */
unsigned int count = 0;
objc_property_t *list =  class_copyPropertyList([Data class], &count);
for (int i = 0; i < count ; i ++)
{
   objc_property_t property = list[i];
   const char *name_property = property_getName(property);//名稱
   const char *name_attributes = property_getAttributes(property);//屬性字符串
   NSLog(@"property:%@  attributes:%@",[NSString stringWithUTF8String:name_property],[NSString stringWithUTF8String:name_attributes]);
   
}
free(list);

//輸出
添加結(jié)果:1
property:number_replace attributes:T@"NSString",N,C,V_secondName
property:secondName  attributes:T@"NSString",V_secondName
property:firstName  attributes:T@"NSString",C,N,V_firstName

參考鏈接:Defining Classes迈窟、Encapsulating DataDeclared Properties忌栅、Type Encodings车酣、Objective-C Runtime

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市索绪,隨后出現(xiàn)的幾起案子湖员,更是在濱河造成了極大的恐慌,老刑警劉巖瑞驱,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件娘摔,死亡現(xiàn)場離奇詭異,居然都是意外死亡钱烟,警方通過查閱死者的電腦和手機晰筛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拴袭,“玉大人读第,你說我怎么就攤上這事∮悼蹋” “怎么了怜瞒?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長般哼。 經(jīng)常有香客問我吴汪,道長,這世上最難降的妖魔是什么蒸眠? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任漾橙,我火速辦了婚禮,結(jié)果婚禮上楞卡,老公的妹妹穿的比我還像新娘霜运。我一直安慰自己脾歇,他們只是感情好,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布淘捡。 她就那樣靜靜地躺著藕各,像睡著了一般。 火紅的嫁衣襯著肌膚如雪焦除。 梳的紋絲不亂的頭發(fā)上激况,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天,我揣著相機與錄音膘魄,去河邊找鬼乌逐。 笑死,一個胖子當著我的面吹牛瓣距,可吹牛的內(nèi)容都是我干的黔帕。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蹈丸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了呐芥?” 一聲冷哼從身側(cè)響起逻杖,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎思瘟,沒想到半個月后荸百,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡滨攻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年够话,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片光绕。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡女嘲,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出诞帐,到底是詐尸還是另有隱情欣尼,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布停蕉,位于F島的核電站愕鼓,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏慧起。R本人自食惡果不足惜菇晃,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蚓挤。 院中可真熱鬧磺送,春花似錦驻子、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至甲捏,卻和暖如春演熟,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背司顿。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工芒粹, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人大溜。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓化漆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親钦奋。 傳聞我的和親對象是個殘疾皇子座云,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 今天終于踏出了第一步,第一次拿產(chǎn)品去和商家談付材,但是有西華的陪同朦拖,有了更大的底氣,其實過程非常的順利厌衔,只需要準備好產(chǎn)...
    紅米若水閱讀 238評論 0 0
  • 周末聽了一個身心靈寫作者的分享璧帝。他從大學(xué)開始關(guān)注身心靈方面的書籍,讀了很多書富寿,一開始只是在豆瓣寫自己的讀后感睬隶,把自...
    紅袖飛揚閱讀 382評論 2 2
  • 我不怕看到, 我怕被欺騙; 我不怕被傷害; 我怕被欺騙; 我不怕傷; 我怕臟; 我可以放手,離開; 我怕像個傻瓜…...
    摘一朵星光閱讀 147評論 0 0