天天看點

[IOS]在xcode開發編譯環境中@property關鍵字的了解

關于@property關鍵字,網上搜出來一籮筐,但是總讓人感覺雜亂無章,沒法真正系統地理過一遍,這裡我以自己的了解對這個東東進行系統的倒騰倒騰,哪裡有問題希望各位指出.

首先,請大家先遺忘@property這個關鍵字,看看下面這個例子能否指派并輸出"_name"這個成員變量的值

//MyClass.h
@interface MyClass:NSObject{
	NSString * _name;
}
@end

//MyClass.m
@implementation MyClass
@end

//main.mm
MyClass *myClass = [[MyClass alloc] init];
myClass.name = @"Jack";
NSLog(@"myClass Name is:%@",myClass.name);           

很明顯Xcode肯定直接顯示"myClass.name = @"Jack";"這句錯誤.

第二步接着我們對MyClass.h和MyClass.m稍做修改,改成如下

//MyClass.h
@interface MyClass:NSObject{
	NSString * _name;
}
- (NSString*) name;
- (void) setName:(NSString*)newName;
@end

//MyClass.m
@implementation MyClass
- (NSString*) name{
	return _name;
}

- (void) setName:(NSString*)newName{
	if (_name != name) {
		[_name release];
		_name = [newName copy];
    }
}@end

//main.mm
MyClass *myClass = [[MyClass alloc] init];
myClass.name = @"Jack";
NSLog(@"myClass Name is:%@",myClass.name);
           

這時我們發現執行成功了,輸出了Jack,如果檢視記憶體,其實還可以發現myClass這個對象的_name這個成員變量值也變成了@"Jack",為什麼會這樣?我的了解是這樣,在xcode的編譯器中,對于"."的這種寫法,編譯器會去自動搜尋類中有沒有對應定義的getter和setter函數,此例中"myClass.name"這種寫法,編譯器就會去搜尋有沒有形如"- (id) name"和"- (void) setName:(id)newName;"的方法,有的話則相應調用該方法,沒的話則報錯.是以才會出現我們以上舉的這兩個例子的情況.

第三步我們繼續對MyClass.h和MyClass.m稍做修改,去掉我們前面第二步加的兩個方法,加上@property和@@synthesize關鍵字,修改後如下:

//MyClass.h
@interface MyClass:NSObject{
	NSString * _name;
}
@property(nonatomic,copy) NSString *name;
@end

//MyClass.m
@implementation MyClass
@synthesize name = _name;
@end

//main.mm
MyClass *myClass = [[MyClass alloc] init];
myClass.name = @"Jack";
NSLog(@"myClass Name is:%@",myClass.name);           

我們發現此時照樣執行成功,這個就是因為@property和@synthesize的貢獻啦,在xcode編譯器中,@property和@synthesize其實就是一種關鍵字,當編譯器看到這個關鍵字的時候,編譯器會自動為我們聲明和實作我們前面第二步手動增加的那兩個方法(當然這裡也可能隻有一個getter方法,或者在setter方法中實作可能和我們前面寫的不一樣,這個待後續講property屬性時再作具體說明),是以達到了我們第二步中一樣的效果.

這裡有兩點需要額外說明下:第一,假設我們在MyClass.h檔案中沒有聲明"_name"這個成員變量,那麼編譯器當看到@synthesize name = _name時會自動幫我們聲明"_name"這個成員變量;第二,在xcode4.4之後,我們可以省略@synthesize關鍵字,隻要我們寫了@property(nonatomic,copy) NSString *propertyName,編譯器會自動幫我們加上類似"@synthesize propertyName = _propertyName"的語句.

如果大家能了解下面兩個例子,那麼我前面說的大家應該也差不多了解了.

例子一

//MyClass.h
@interface MyClass:NSObject{
	NSString * _nnnnnnnnname;
}
@property(nonatomic,copy) NSString *name;
@end

//MyClass.m
@implementation MyClass
@synthesize name = _nnnnnnnnname;
@end

//main.mm
MyClass *myClass = [[MyClass alloc] init];
myClass.name = @"Jack";//此時成員變量_nnnnnnnnname變成了@"Jack"
NSLog(@"myClass Name is:%@",myClass.name);//此時輸出其實是_nnnnnnnnname這個成員變量的值           

例子二

//MyClass.h
@interface MyClass:NSObject{
	NSString * _name;
}
@property(nonatomic,copy) NSString *name;
- (NSString*) name;
- (void) setName:(NSString*)newName;
@end

//MyClass.m
@implementation MyClass
@synthesize name = _name;
- (NSString*) name{
	NSLog(@"My redefined getter func");
	return _name;
}

- (void) setName:(NSString*)newName{
	NSLog(@"My redefined setter func");
	if (_name != name) {
		[_name release];
		_name = [newName copy];
    }
}
@end

//main.mm
MyClass *myClass = [[MyClass alloc] init];
myClass.name = @"Jack";//此時會輸出"My redefined setter func",因為由@property關鍵字導緻的編譯器自動生成的setter函數被覆寫了
NSLog(@"myClass Name is:%@",myClass.name);//此時會輸出"My redefined getter func",因為由@property關鍵字導緻的編譯器自動生成的getter函數被覆寫了           

下面我們來談談@property的修飾屬性.

根據我目前的了解,@property的修飾屬性有三類:隻讀性(readwrite/readonly);原子性(nonatomic/atomic);寫記憶體方法(assign/retain/copy/weak/strong).

隻讀性:顧名思義,表示該屬性是否隻讀,預設是readwrite,即編譯器會生成getter和setter方法,若設定為readonly,編譯器隻會生成getter方法.

原子性:和Java中原子性一個概念,表示是否支援類似多線程時鎖定等待.預設atomic即啟動線程保護,有線程對該值讀寫未完成時鎖定不被其他線程通路.

寫記憶體方法性:

//assign方式,在設值時替換新舊變量(預設),适用簡單資料類型,如:NSIngeter/CGFloat/int/char等
//這裡不确定對象能否支援assign
- (void) setName:(NSString*)newName{
	if (_name != newName) {
		_name = newName;
    }
}
//retain方式,在設值時retain新的變量,release舊變量,适用類對象,除了NSString這種特殊的類對象
- (void) setName:(NSString*)newName{
	NSLog(@"My redefined setter func");
	if (_name != name) {
		[_name release];
		_name = [newName retain];
    }
}
//copy方式,在設值時copy一份新變量,release舊變量,一般用于NSString
- (void) setName:(NSString*)newName{
	NSLog(@"My redefined setter func");
	if (_name != name) {
		[_name release];
		_name = [newName copy];
    }
}
//網上找來一句話,參考參考吧
//assign/retain/weak/strong,在非ARC環境下,assign為預設,引用計數不變;retain引用計數加1;
//在引用計數環境下,預設為strong,與retain作用相同;從5.0系統後引入weak,作用與assign相似,不過當所指向對象引用為0時,自動置為nil           

繼續閱讀