編寫高質(zhì)量代碼是所有程序員的追求余耽,《編寫高質(zhì)量代碼》這本書對于改善Objective-C程序給了61建議惶凝。下面是個人的筆記蝉稳。
1.盡量使用const、enum來替換預(yù)處理#define
相對于字符串字面量或數(shù)字涮总,使用用常量來定義。
例如:
statis NSString *const kPageRefreshNotification = @"PageRefreshNotification";
statis const CGFloalt kTableViewHeight = 50.0;
enum兼有#define和const的所有優(yōu)點祷舀,但只能用在整型中瀑梗,所以在定義一個整型時,可以考慮使用枚舉(enum)
例如:
enum{
MY_INT_CONST = 12345,
}
要點:
(1)盡量避免使用#define預(yù)處理命令裳扯。#define預(yù)處理命令不包括任何類型信息抛丽,僅僅是在編譯前做替換操作。他們在重復(fù)定義是不會發(fā)出警告饰豺,容易在整個程序中產(chǎn)生不一致的值亿鲜。
(2)在源文件(.m)中定義static const 類型常量應(yīng)為無需全局引用,所以他們的名字不需要包含命名空間冤吨。
(3)在頭文件(.h)中定蒿柳;已的全局引用的常量,需要關(guān)聯(lián)定義在源文件(.m)中的部分漩蟆。因為需要被全局引用垒探,所以他們的名字需要包含命名空間,通常用他們的類名作為命名前綴怠李。
(4)盡量用ES_ENUM和NS_OPTIONS宏來實現(xiàn)枚舉圾叼。
2.利用鍵-值機制訪問類的私有成員變量和方法
在Objective-C中仔引,類的成員變量或方法是沒有絕對私有的,可以借助“runtime”機制進(jìn)行對他們訪問褐奥,鍵-值機制有三個常見的成員——鍵-值綁定(KVB)咖耘、鍵-值編碼(KVC)和鍵-值觀察(KVO)
KVC(Key-Value-Coding)
KVC主要利用一種使用字符串標(biāo)識符,間接訪問對象屬性的機制撬码,它是很多技術(shù)的基礎(chǔ)儿倒。主要有(setValue:forKey,valueForKey)和(setValue:forKeyPath呜笑,valueForKeyPath)兩種方法夫否。這兩種方法的使用用途,可以通過如下的代碼來體現(xiàn):
@interface Phone
{
NSStirng * phoneName;
}
@end
@interface Person
{
NSStirng* myName;
Phone *myPhone叫胁;
}
@end
@implementation Person
Phone *iPhone = ..... //phone對象
Person *xiaoming = ....//person對象
NSString *s1 = [iPhone valueForKey:@"phoneName"];
NSString *s2 = [xiaoming valueForKey:@"myName"];
NSString *s4 = [xiaoming valueForKeyPath:@"myPhone.phoneName"];//正確?
NSString *s5 = [xiaoming valueForKey:@"myPhone.phoneName"];//錯誤?
@end
注意:valueForKeyPath的值是一個路徑(路徑之間以點.分割)凰慈,比如數(shù)據(jù)成員就是對象自己,尋值過程就會向下深入下去驼鹅。
KVC實現(xiàn)原理是運用一個isa-swizzing技術(shù)微谓。isa-swizzing就是類型混合指針機制,KVC通過isa-swizzing來實現(xiàn)其內(nèi)部定位输钩。
isa指針指向的是對象的類豺型,這個類也是一個對象,有自己的權(quán)限买乃。是根據(jù)類的定義編譯而來姻氨。類對象負(fù)責(zé)維護(hù)一個方法調(diào)度表,非標(biāo)本職上是指向類方法的指針組成的剪验;類對象中還保留一個基類的指針肴焊,該指針又有自己的方法調(diào)度表和基類(還有所有通過集成得到的公共和保護(hù)的實例變量)。isa指針對消息分發(fā)機制和Cocoa對象的動態(tài)能力很關(guān)鍵功戚。
例如下面的一行KVC代碼:
[dict setValue:@"hello" forKey:@"nihao"];
就會被處理器編譯成:
SEL sel = sel_get_uid ("setValue:forKey:");
IMP method = objc_msg_lookup (dict->isa,sel);
method(dict,sel,@"hello",@"nihao");
KVO(Key-Value Observing)
KVO通過實現(xiàn)名為NSKeyValueObserving的非正式協(xié)議娶眷,其作用是使對象可以將自己注冊為其它對象的觀察者。但被觀察對象的屬性之一發(fā)生改變時疫铜,會直接通知對應(yīng)的觀察者茂浮。Cocoa為遵循KVO對象的每個屬性,都實現(xiàn)了自動觀察者通知機制壳咕。
使用KVO席揽,通常遵循如下流程:
1)注冊與解除注冊
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context NS_AVAILABLE(10_7, 5_0);
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
這三個方法的定義在Foundation/NSKeyValueObserving.h中,NSObject谓厘、NSArray幌羞、NSSet均實現(xiàn)以上方法,因此我們不僅可以觀察普通對象竟稳,還可以觀察數(shù)組和集合類對象属桦。注意熊痴,不要忘了解除注冊,否則會造成資源泄漏聂宾。
2)處理變更通知
當(dāng)被觀察這類對象中的某個屬性發(fā)生變更時果善,光插著需要處理接收到的變更通知。在觀察類中系谐,需要實現(xiàn)名為NSKeyValueObserving的category方法:
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context;
其中巾陕,change這個字典保存了哪些變更的信息取決于注冊時的NSKey-ValueObserving-Options
typedef NS_OPTIONS(NSUInteger, NSKeyValueObservingOptions) {
NSKeyValueObservingOptionNew = 0x01,
NSKeyValueObservingOptionOld = 0x02,
NSKeyValueObservingOptionInitial NS_ENUM_AVAILABLE(10_5, 2_0) = 0x04,
NSKeyValueObservingOptionPrior NS_ENUM_AVAILABLE(10_5, 2_0) = 0x08
};
3)手動或者自動實現(xiàn)KVO通知
KVO機制提供兩種變更消息通知模式:手動和自動實現(xiàn)。在NSKeyValueObservingCustiomization的category中有方法:
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key;
默認(rèn)情況下纪他,KVO是自動實現(xiàn)的鄙煤。其會自動調(diào)用NSKeyValueObserverNotification的category方法:
- (void)willChangeValueForKey:(NSString *)key;
- (void)didChangeValueForKey:(NSString *)key;
或:
- (void)willChange:(NSKeyValueChange)changeKind valuesAtIndexes:(NSIndexSet *)indexes forKey:(NSString *)key;
- (void)didChange:(NSKeyValueChange)changeKind valuesAtIndexes:(NSIndexSet *)indexes forKey:(NSString *)key;
又或:
- (void)willChangeValueForKey:(NSString *)key withSetMutation:(NSKeyValueSetMutationKind)mutationKind usingObjects:(NSSet *)objects;
- (void)didChangeValueForKey:(NSString *)key withSetMutation:(NSKeyValueSetMutationKind)mutationKind usingObjects:(NSSet *)objects;
KVB(Key-Value Binding)
KVB負(fù)責(zé)建立對象之間的綁定關(guān)系,以及移除和公布這種綁定關(guān)系茶袒。它用了幾個非正式的協(xié)議梯刚。屬性的綁定必須制定一個對象和一個指向該屬性的鍵路徑。
KVB實現(xiàn)的兩個基本方法如下薪寓。
(1)為對象添加觀察者:
OBserver addObserver:forKeyPath:(NSString *)keyPath options: context:;
(2) 觀察者OBserver收到信息的處理函數(shù)
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
KVB和KVO最明顯的使用場景就是在一些界面實時顯示很強的地方亡资,如股票走向、售票余額等预愤,這種方式免去了自己操作通知的麻煩沟于。
要點:
(1)在Objective-C中,類的成員變量或方法是沒有絕對私有的植康,可以借助”編譯運行時“機制,即”瞎子摸黑“機制來實現(xiàn)對他們的訪問展懈。
(2)KVC和KVO在定制子類的設(shè)計時特別重要销睁。
(3)KVC、KVO和KVB都支持遍歷存崖。
(4)KVC主要通過isa指針來實現(xiàn)其內(nèi)部查找定位冻记。KVO其設(shè)計基于設(shè)計模式中的”觀察者模式“。KVB和KVO最明顯的使用場景就是在一些界面實時顯示很強的地方来惧。