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如圖所示
其中Attributes類型如下
其中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 Data、Declared Properties忌栅、Type Encodings车酣、Objective-C Runtime