編寫高質量iOS與OS X代碼的52個有效方法(一)
編寫高質量iOS與OS X代碼的52個有效方法(三)
對象劫灶、消息筛严、運行期
6、理解“屬性”這一概念
“屬性”是OC的一項特性句喷,用于封裝對象中的數(shù)據(jù)。OC對象通常會把其所需要的數(shù)據(jù)保存為各種實例對象俱恶。實例對象一般通過“存取方法”來訪問帖族。其中,“獲取方法(getter)”用于讀取變量值逊抡,而“設置方法(setter)”用于寫入變量值。
屬性特質
@property (nonatomic, readwrite, copy) NSString *firstName;
屬性可以擁有的特質分為四類:
原子性
在默認情況下零酪,由比編譯器合成的方法會通過鎖定機制確保其原子性冒嫡。如果屬性具備nonatomic特質,則不使用同步鎖四苇。請注意孝凌,盡管沒有名為“atomic”的特質(如果某屬性不具備nonatomic特質,那他就是“原子的”(atomic))月腋,但是仍然可以在屬性特質中寫明這一點蟀架,編譯器不會報錯。若是自己定義存取方法榆骚,那么久應該遵從與屬性特質享福的原子性片拍。
讀/寫權限
- 具備readwrite特質的屬性擁有“獲取方法(getter)與設置方法(setter)”。若該屬性由@synthesize實現(xiàn)妓肢,則編譯器會自動生成這兩個方法捌省。
- 具備readonly特質的屬性僅擁有獲取方法,只有當該屬性由@synthesize實現(xiàn)時碉钠,編譯器才會為其合成獲取方法所禀。你可以用此特質把某個屬性對外公開為只讀屬性,然后再“class-aontonuation”分類中將其重新定義為讀寫屬性放钦。
內(nèi)存管理語義
屬性用于封裝數(shù)據(jù),而數(shù)據(jù)則要有“具體的所有權語義”恭金。
- assign “設置方法”只會執(zhí)行針對“純量類型”(例如:CGFloat或NSInteger等)的簡單賦值操作操禀。
- strong 此特質表明該屬性定義了一種“擁有關系”。為這種屬性設置新值時横腿,設置方法會先保留新值颓屑,并釋放舊值,然后再將新值設置上去耿焊。
- weak 次特質表明該屬性定義了一種“非擁有關系”揪惦。為這種屬性設置新值時,設置方法既不保留新值罗侯,也不釋放舊值器腋。此特質同assign類似,然而再屬性所指的對象遭到摧毀時,屬性值也會清空(nil out)纫塌。
- unsafe_unretained 此特質的語義和assign相同诊县,但是他適用于“對象類型”,該特質表達一種“非擁有關系”(“不保留”措左, unretained)依痊,當目標對象遭到摧毀時,屬性值不會自動清空(“不安全”怎披, unsafe)胸嘁,這一點與weak有區(qū)別。
- copy 此特質所表達的所屬關系與strong類似凉逛。然而設置方法并不保留新值性宏,而是將其“拷貝(copy)”。當屬性類型為NSString時鱼炒,經(jīng)常用此特質來保護其封裝性衔沼,因為傳遞給設置的新值有可能指向一個NSMutableString類的實例。所以要“拷貝”一個”不可變“的字符串昔瞧,確保對象中的字符串值不會無意間變動指蚁。
方法名
- getter=<name>
@peroperty (nonatomic, getter=isOn) BOOL on;
- setter=<name>
要點
- 可以用@property語法來定義對象中所封裝的數(shù)據(jù)。
- 通過“特質”來指定存儲數(shù)據(jù)所需的正確語義自晰。
- 在設置屬性所對應的實例變量時凝化,一定要遵守從該屬性所聲明的語義。
- 開發(fā)iOS程序時應該使用nonatomic屬性酬荞,因為atomic屬性會嚴重影響性能搓劫。
7、在對象內(nèi)部盡量直接訪問實例變量
@interface Eoperson : NSObject
@property (nonatomic, copy) NSString *firstName;
@property (nonatomic, copy) NSString *lastName;
- (NSString*)fullName;
- (void)setFullName:(NSString*)fullName;
@end
//fullName與setFullName這兩個方法可以這樣實現(xiàn)
- (NSString*)fullName {
return [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName];
}
- (void)setFullName:(NSString*)fullName {
NSSArray *components = [fullName componnentsSeparatedByString:@" "];
self.firstName = [components objectAtIndex: 0];
self.lastName = [components objectAtIndex: 1];
}
在fullName的獲取方法與設置方法中,我們使用"點語法"混巧,通過存取方法來訪問相關的實例變量∏瓜颍現(xiàn)在假設重寫這兩個方法,不經(jīng)由存取方法,而是直接訪問實例變量
- (NSString*)fullName {
return [NSString stringWithFormat:@"%@ %@", _firstName, _lastName];
}
- (void)setFullName:(NSString*)fullName {
NSSArray *components = [fullName componnentsSeparatedByString:@" "];
_firstName = [components objectAtIndex: 0];
_lastName = [components objectAtIndex: 1];
}
這兩種方法的區(qū)別
- 由于不經(jīng)過Objective-C的“方法派對”,所以直接訪問實例變量的速度當然比較快咧党。在這種情況下秘蛔,編譯器所生成的代碼塊會直接訪問保存對象實例變量的那塊內(nèi)存。
- 直接訪問實例變量傍衡,不會調用其“設置方法”深员,這就繞過了對相關屬性定義的“內(nèi)存管理語義”。比方說蛙埂,如果在ARC下直接訪問一個聲明為copy的屬性倦畅,那么并不會拷貝該屬性,只會保留新值并釋放新值绣的。
- 如果直接訪問實例變量叠赐,不會觸發(fā)“鍵值觀察(KVO)”欲账。這樣做是否會產(chǎn)生新的問題,還取決具體的對象行為燎悍。
- 通過屬性來訪問有助于排查與之相關的錯誤敬惦,因為可以給“獲取方法”和“設置方法”添加斷點,監(jiān)控該屬性的調用者及訪問時機谈山。
要點
- 在對象讀取內(nèi)部數(shù)據(jù)時俄删,應該直接用過實例變量來讀,而寫入數(shù)據(jù)的時候奏路,則通過屬性來寫畴椰。
- 在初始化和dealloc方法中,總是應該直接通過實例變量來讀寫數(shù)據(jù)鸽粉。
- 有時會用懶惰性初始化技術配置某分數(shù)據(jù)斜脂,這種情況下,需要通過屬性來讀取數(shù)據(jù)触机。
8帚戳、理解“對象等同性”這一概念
特定類所具有等同性判定方法
例如:“isEqualToArray:”“isEqualToDictionary”
等同性判定的執(zhí)行深度
創(chuàng)建等同性判定方法時,需要決定是根據(jù)整個對象來判斷等同性儡首,換是根據(jù)其中幾個字段來判斷片任。NSA仍然有的檢測方式為先看兩個數(shù)組包含的對象個數(shù)是否相同,若相同蔬胯,則在兩個對應位置的兩個對象身上調用“isEqual:”方法对供。如果對象位置身上均相等,那么這兩個數(shù)組就想等氛濒,這就叫做“深度等同性判定”产场。不過有時候無須降所有數(shù)據(jù)逐個比較,只根據(jù)其中部分數(shù)據(jù)即可判定二者是否相同舞竿。
假如京景,一個Persion類是根據(jù)數(shù)據(jù)庫里的數(shù)據(jù)創(chuàng)建出來的,那么其中就可能會含有另外一個屬性骗奖,此屬性是“唯一標識符”醋粟,那么我們只需要檢測兩個對象中的標識符是否相同就能判定兩個對象是否相等。
容器中可變類的等同性
容器中放入可變對象的時候重归,就不應在改變其哈希碼了。
要點
- 若想檢測對象的等同性厦凤,請?zhí)峁癷sEqual:”與hash方法鼻吮。
- 相同的對象必須具有相同的哈希碼,但是兩個哈希碼相同的對象卻未必相同较鼓。
- 不要盲目的諸葛檢測每個屬性椎木,而是應該依照具體需求來制定監(jiān)測方案违柏。
- 編寫hash方法時,應該使用計算速度快而哈希碼碰撞幾率低的算法香椎。