IOS 開發學習總結objective-c物件導向之——合成存取方法與點文法
合成存取方法
前面我們介紹了為成員變數自己實現 setter 方法和 getter 方法,但如果一個了類中有很多成員變數時,會非常的不便。
objective-c從 OC 2.0版本開始,自動合成了setter 方法和 getter 方法。而且,如果開發人員需要自己控制某個setter 方法和 getter 方法的實現時,可以自己提供 setter 方法和 getter 方法,開發人員提供的setter 方法和 getter 方法會覆蓋系統自動合成的相應方法。
使系統自動合成 setter 和 getter 的方法的步驟在類介面部分使用@property 指令定義屬性。——@property 指令放在屬性定義的前面,直接放在@interface,@end 之間定義。 在類實現的部分使用@synthesize 指令聲明該屬性。
當採用上面的2步後,會合成成對的 setter 和 getter 方法。還會自動在類的實現部分定義一個與 getter 方法同名的成員變數。這部分可以參見之前的一篇博文《【IOS 開發學習總結-OC-11】★objective-c物件導向之——封裝和存取控制符 》
如果為某個類定義了一個成員變數,並提供了相應的setter 和 getter 方法,那麼可以稱為定義了一個屬性(property)。
Xcode4.0後的編碼規範推薦將成員變數名定義為以底線開頭,比如在 Xcode 的模板代碼中有這樣的代碼:
@synthesize window=_window;
該代碼的作用是:告訴系統合成的property對應的成員變數為_window, 而不是 window。
@synthesize的文法格式:
@synthesize property名 =成員變數名;
或者是
@synthesize property名
;
@synthesize 後如果沒有有指定成員變數名,成員變數預設與合成的 getter 方法重名。
示範合成存取方法的用法。範例程式碼:
FKUser.h檔案:
#import @interface FKUser : NSObject// 使用@property定義3個property@property (nonatomic) NSString* name;@property NSString* pass;@property NSDate* birth;@end
FKUser.m檔案:
#import FKUser.h@implementation FKUser// 為3個property合成setter和getter方法,// 指定name property底層對應的成員變數名為_name@synthesize name = _name;@synthesize pass;@synthesize birth;// 實現自訂的setName:方法,添加自己的控制邏輯- (void) setName:(NSString*) name{ self->_name = [NSString stringWithFormat:@+++%@ , name];}@end
FKUserTest.m檔案:
#import FKUser.hint main(int argc , char * argv[]){ @autoreleasepool{ // 建立FKUser對象 FKUser* user = [[FKUser alloc] init]; // 調用setter方法修改user成員變數的值 [user setName:@admin]; [user setPass:@1234]; [user setBirth:[NSDate date]]; // 訪問user成員變數的值 NSLog(@管理員帳號為:%@,密碼為:%@,生日為:%@ , [user name] , [user pass] , [user birth]); }}
程式通過@property,@synthesize 合成 setter,getter 方法後,程式就可以在後面通過setter,getter 方法存取成員變數的值。
定義@property時用到的特殊指示符assign
assign:指定對屬性只是簡單賦值,不更改對所賦的值的引用計數。主要適用於:NSInteger 等基本類型,以及 short,float,結構體等各種 C 資料類型。那麼問題來了,
為什麼NSInteger 等基本類型,以及 short,float,結構體等各種 C 資料類型不存在回收的問題呢?
iOS中的垃圾處理機制是根據一個對象的索引數(引用計數)來處理的,為0的時候表示沒有地方使用該對象,則該對象將被清除,而基礎資料型別 (Elementary Data Type)不屬於對象,它的建立和使用都是在記憶體中,超出對應方法體即被清除,所以不需要使用垃圾處理機制,無需記錄索引值(計數),不存在回收的問題。所以使用assgin。
atomic與nonatomic
atomic(nonatomic):指定合成的存取方法是否為原子操作。預設為atomic。
如果用atomic,那麼合成的存取方法都是安全執行緒的——當一個線程進入存,取方法的方法體後,其他線程無法進入該存,取方法,這樣可以避免多線程破壞對象的資料完整性。
copy
copy:使用 copy 指示符時,當調用 setter 方法對成員變數賦值時,會將被賦值的對象複製一個副本,再將副本賦值給成員變數。copy 會將原成員變數所引用對象的引用計數減1。
何時考慮使用 copy?
當成員變數的類型是可變類型或其子類是可變類型時,被賦值的對象可能在賦值之後被修改,如果程式不需要這種修改影響 setter 方法設定的成員變數的值,此時考慮用 copy 指示符。
樣本程式:
FKBook.h
#import @interface FKBook : NSObject// 使用@property定義1個property@property (nonatomic) NSString* name;@end
FKBook.m
#import FKBook.h@implementation FKBook@synthesize name;@end
FKBookTest.m
#import FKBook.hint main(int argc , char * argv[]){ @autoreleasepool{ FKBook* book = [[FKBook alloc] init]; //NSMutableString是NSString的子類 NSMutableString* str = [NSMutableString stringWithString:@iOS講義]; // 對book的name屬性賦值 [book setName:str]; // 輸出book的name屬性 NSLog(@book的name為:%@ , [book name]); // 修改str字串 [str appendString:@是一本很好的iOS學習圖書]; // 下面代碼將會看到book的name屬性也會被修改 NSLog(@book的name為:%@ , [book name]); }}
編譯運行該程式後,輸出內容為:
book的name為:iOS講義
book的name為:iOS講義是一本很好的iOS學習圖書
如果我們不想 FKBook 的 name 屬性不會隨著NSMutableString對象的改變而改變,我們可以在定義 name 屬性的一行增加copy 指示符就可以了, @property (nonatomic,copy) NSString* name;
setter&getter
用於為合成的setter,getter方法指定自訂的方法名。
舉個栗子:getter=abc
指定 getter 方法名為 abc,setter=xyz:
,則指定 setter 方法名為 xyz。注意:——setter方法不要忘了帶冒號,因為它要帶參數。
樣本程式:
FKItem.h
#import @interface FKItem : NSObject// 使用@property定義1個property,並指定自訂的getter、setter方法名@property (assign , nonatomic , getter=wawa , setter=nana:) int price;@end
FKItem.m
#import FKItem.h@implementation FKItem@synthesize price;@end
FKItemTest.m
#import FKItem.hint main(int argc , char * argv[]){ @autoreleasepool{ FKItem* item = [[FKItem alloc] init]; // 設定price屬性 [item nana:30]; // 訪問price屬性 NSLog(@item的price為:%d , [item wawa]); }}
這裡的的 setPrice: 方法和 擷取的price 方法已經不存在,取而代之的是 nana 和 wawa。
readonly&readwrite
readonly指示系統只合成 getter 方法,不再合成 setter 方法。readwrite是預設值,2個方法都合成。
retain
使用retain指示符定義屬性時,當把某個對象賦值給該屬性時,該屬性原來所引用的對象的引用計數-1,被賦值對象的引用計數+1.
說明:在啟動 ARC 的情況下,一般少用 retain。但在未啟用 ARC 的情況下,retain是很有用的。
樣本程式:(下面的程式關閉了 ARC)
FKWin.h
#import @interface FKWin : NSObject// 使用@property定義1個property@property (nonatomic , retain) NSDate* date;@end
FKWin.m
#import FKWin.h@implementation FKWin@synthesize date;@end
FKWinTest.m
#import FKWin.hint main(int argc , char * argv[]){ FKWin* win = [[FKWin alloc] init]; NSDate* date = [[NSDate alloc] init]; // 第一次賦值時,date的引用計數為1 NSLog(@date的引用計數為:%ld , date.retainCount); // 由於使用了retain指示符,賦值時導致date的引用計數+1 [win setDate:date]; // 下面輸出的引用計數為2 NSLog(@[win date]的引用計數為:%ld , [win date].retainCount); // 釋放date的引用計數,date的引用計數-1 [date release]; // 下面輸出的引用計數為1 NSLog(@[win date]的引用計數為:%ld , [win date].retainCount);}
點文法
objective-c 允許使用簡化的點文法訪問屬性和對屬性賦值。其本質仍是調用 getter,setter 方法。
樣本程式:
FKCard.h
#import @interface FKCard : NSObject// 使用@property定義2個property@property (nonatomic , copy) NSString* flower;@property (nonatomic , copy) NSString* value;@end
FKCard.m
#import FKCard.h@implementation FKCard@synthesize flower;@synthesize value;@end
FKCardTest.m
#import FKCard.hint main(int argc , char * argv[]){ @autoreleasepool{ FKCard* card = [[FKCard alloc] init]; // 通過點文法對屬性賦值 card.flower = @♠; card.value = @A; // 通過點文法來訪問屬性值 NSLog(@我的撲克牌為:%@%@, card.flower, card.value); }}