屬性是Objective-C 2.0定義的語法,為實例變量提供了setter、getter方法的默認實現。能在一定程度上簡化程序代碼,並且增強實例變量的訪問安全性。
與類相似,屬性也需要聲明和實現。
屬性的聲明:使用@property聲明屬性
例如:@property NSString *name;
相當於 @interface 中聲明了兩個方法(setter、getter):
- (void)setName:(NSString *)name;
- (NSString *)name;
屬性的實現:使用@synthesize實現屬性
例如:@synthesize name = _name;
相當於 @implementation 實現了setter、getter
- (void)setName:(NSString *)name
{
_name = name;
}
- (NSString *)name
{
return _name;
}
注意:
原先 @property 聲明與實現文件中的另一個聲明 @synthesize 相結合,來完成setter、getter方法的創建。Xcode4.5之後 @synthesize 會在內部自動生成,不用手動添加。但是,如果屬性 @property 聲明之後,要在.m實現文件中重寫setter、getter方法,則必須在.m實現文件中添加 @synthesize。
屬性聲明後,重寫setter、getter方法,聲明@synthesize的示例代碼如下:
#import
@interface Person : NSObject
@property NSString *name;
- (void)setName:(NSString *)name;
- (NSString *)name;
@end
#import Person.h
@implementation Person
/* 屬性聲明後,重寫setter、getter方法,必須聲明@synthesize */
@synthesize name = _name;
- (void)setName:(NSString *)name
{
_name = name;
}
- (NSString *)name
{
return _name;
}
@end
Objective-C提供屬性的目的是為了簡化程序員編碼,為屬性提供了一些關鍵字?以控制setter、getter的實現細節,這些關鍵字我們稱為屬性的屬性(attribute)。屬性的屬性一共3大類。
readonly
,告訴編譯器,只聲明getter方法(無setter方法)。
例如:@property (readonly) NSString *name; //等價於 - (NSString *) name;
readwrite
,告訴編譯器,既聲明setter?聲明getter。
例如: @property (readwrite) NSString *name; //等價於 - (NSString *) name; - (void)setName:(NSString *)name;
,讀寫性控制的默認設置。
setter = 方法名
,指定設置器的方法名。
getter = 方法名
,指定訪問器的方法名。
atomic
,setter、getter方法在多線程訪問下是絕對安全的,即setter、getter內部做了多線程訪問處理。原?子性控制的默認設置是atomic。
nonatomic
,setter、getter方法內部不會做多線程訪問處理,僅僅是普通的setter、getter?法。
程序開發過程中,setter、getter處處都在用,如果使用atomic,需要不斷對setter、getter加鎖解鎖以保證線程訪問安全,會很占用系統資源,降低系統性能。通常設置為nonatomic,某些屬性需要線程安全的時候,才定義為atomic。
例如:
@property (readwrite, nonatomic) NSString *name;
//等價於
- (NSString *)name;
- (void)setName:(NSString *)name;
assign
,setter、getter內部實現是直接賦值,適用於基本數據類型,默認值。
例如:
@property (nonatomic, assign) NSString *name;
- (void)setName:(NSString *)name{
_name = name;
}
- (NSString *)name{
return _name;
}
retain
,setter、getter的內部實現會做內存優化,適用於對象類型。
例如:
@property (nonatomic, retain) NSString *name;
- (void)setName:(NSString *)name{
if(_name != name){
[_name release];
_name = [name retain];
}
}
- (NSString *)name{
return [[_name retain]autorelease];
}
copy
,setter、getter的內部實現也會做內存優化,復制一個副本,適用於特殊的對象類型,一般適用於NSSting。
例如:
@property (nonatomic, copy) NSString *name;
- (void)setName:(NSString *)name{
if(_name != name){
[_name release];
_name = [name copy];
}
}
- (NSString *)name{
return [[_name retain]autorelease];
}
小結:
如果屬性是?對象類型(比如int,float等)屬性的語義設置使用assign。 如果屬性是對象類型(?如NSStrng、NSArray等)屬性的語義設置使用retain。 如果屬性是對象類型並且想得到參數的copy,使用copy關鍵字。點語法是Objective-C 2.0中定義的語法格式。提供了?種便捷的屬性訪問?式。
點語法的使用
凡是符合系統默認setter、getter書寫格式的方法都可以使用點語法。
例如:[person1 setName:@”ZhangSan”];
可以等價寫成person1.name = @”ZhangSan”;
。
NSString *name = [person1 name];
可以等價寫成NSString *name = person1.name;
屬性是一對getter、setter方法,點語法是屬性的另一種調用格式。
KVC(Key-Value-Coding),鍵值編碼,是一種間接訪問實例變量的方法。
key:鍵,用於標識實例變量。
vlaue:實例變量對應的值。
修改值
setValue:forKey: setValue:forKeyPath: setValue:forUndefinedKey: setValuesForKeysWithDictionary:獲取值
valueForKey: valueForKeyPath: valueForUndefinedKey:注意事項
當key不存在的時候,會執行setValue:forUndefinedKey:
系統默認實現是拋出一個異常
為了更好的體驗KVC的用途,以下是示例代碼:
要求
定義一個Classroom類: 實例變量: 教室編號, 學生(Person類)
實例變量不寫setter, getter方法
Classroom.h文件代碼如下:
#import
@class Person;
@interface Classroom : NSObject
{
@protected
NSString *_classNum;
Person *_student;
}
#pragma mark - 重寫父類方法
- (void)setValue:(id)value forUndefinedKey:(NSString *)key;
- (id)valueForUndefinedKey:(NSString *)key;
@end
Classroom.m文件代碼如下:
#import Classroom.h
#import Person.h
@implementation Classroom
- (void)setValue:(id)value forUndefinedKey:(NSString *)key
{
NSLog(@無此實例變量: %@, key);
}
- (id)valueForUndefinedKey:(NSString *)key
{
NSLog(@無此實例變量: %@, key);
return nil;
}
@end
Person.h文件代碼如下:
#import
@interface Person : NSObject
/* 實例變量 */
{
@protected
NSString *_name;
NSInteger _age;
}
#pragma mark - 屬性的聲明
/* 代碼規范: 屬性的名字沒有 _
* 屬性包含三部分內容: 實例變量, 實例變量的setter方法, 實例變量的getter方法
*/
@property NSString *hobby;
@property NSString *sex;
#pragma mark - 屬性的屬性(特性)
/* 第一類 attribute: 讀寫性控制
* readonly: 相當於只提供了getter方法
* readwrite: 相當於提供了setter 和 getter方法
* 默認: readwrite
* 通常設置: readwrite
*/
//@property (readonly) NSString *name;
/* 第二類: attribute: 原子性控制
* atomic: 原子性
* nonatomic: 非原子性
* 默認: atomic
* 通常設置: nonatomic
*/
@property (readwrite, nonatomic) NSString *height;
/* 第三類 attribute: 語義設置
* assign: 非對象類型使用: int, float, NSInteger
* retain: 對象類型使用: NSString, NSArray, 會對setter, getter方法進行內存優化
* copy: 對象類型使用, 對setter, getter方法進行內存優化
* 默認: assign
*/
@property (readwrite, nonatomic, assign) NSInteger weight;
@property (readwrite, nonatomic, retain) NSString *marry;
@property (readwrite, nonatomic, copy) NSString *work;
/* 通常的屬性聲明 */
@property (nonatomic, retain) NSString *blood;
#pragma mark -
/* 實例變量setter, getter方法 */
- (void)setName:(NSString *)name;
- (NSString *)name;
- (void)setAge:(NSInteger)age;
- (NSInteger)age;
@end
Person.m文件代碼如下:
#import Person.h
@implementation Person
#pragma mark - 屬性的實現
/* 相當於實現了 setter 和 getter 方法
* 可以省略 @synthesize 一行
*/
@synthesize hobby = _hobby;
@synthesize sex = _sex;
- (void)setName:(NSString *)name
{
_name = name;
}
- (NSString *)name
{
return _name;
}
- (void)setAge:(NSInteger)age
{
_age = age;
}
- (NSInteger)age
{
return _age;
}
@end
main.m文件代碼如下:
#import
#import Person.h
#import Classroom.h
int main(int argc, const char * argv[]) {
@autoreleasepool {
/* 定義一個Classroom類: 實例變量: 教室編號, 學生(Person類)
* 實例變量不寫setter, getter方法
*/
/* 創建一個Person對象, 一個Classroom對象 */
Person *stu = [[Person alloc] init];
Classroom *classroomOne = [[Classroom alloc] init];
/* 使用KVC賦值 */
[classroomOne setValue:@5211 forKey:@_classNum];
[classroomOne setValue:stu forKey:@_student];
[classroomOne setValue:@5212 forKey:@classroomNum];
[classroomOne setValue:@dd forUndefinedKey:@sss];
[classroomOne setValue:@ZhangSan forKeyPath:@_student.name];
/* 使用KVC取值 */
NSLog(@classNum: %@, [classroomOne valueForKey:@_classNum]);
NSLog(@student name: %@, [classroomOne valueForKeyPath:@_student.name]);
NSLog(@student name: %@, [classroomOne valueForKeyPath:@_student002]);
}
return 0;
}