部分代碼均可參考AFNetworking
文件
1.KVO監聽屬性(自動監聽)
1.1.注冊監聽
AFURLSessionManager.m 156
[progress addObserver:self
forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
options:NSKeyValueObservingOptionNew
context:NULL];
1.2.監聽回調
AFURLSessionManager.m 171
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
if ([object isEqual:self.downloadProgress]) {
if (self.downloadProgressBlock) {
self.downloadProgressBlock(object);
}
}
else if ([object isEqual:self.uploadProgress]) {
if (self.uploadProgressBlock) {
self.uploadProgressBlock(object);
}
}
}
1.3.移除監聽(如果在對象監聽對象釋放的時候,監聽沒有移除,程序會奔潰)
- (void)dealloc {
[self.downloadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
[self.uploadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
}
2.KVO監聽屬性(手動監聽)
手動監聽與自動監聽幾乎一樣,都有上面的三個步驟,只是,手動監聽,需要實現下列方法
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key
這個方法接收監聽的key,返回YES表示使用自動監聽,而返回NO則是手動監聽,一般手動監聽需要實現監聽key的set方法,例如
AFURLRequestSerialization.m 266
- (void)setAllowsCellularAccess:(BOOL)allowsCellularAccess {
[self willChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))];
_allowsCellularAccess = allowsCellularAccess;
[self didChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))];
}
手動監聽相對于自動監聽有如下優點,可以避免不必要的通知,例如將上述代碼修改如下
- (void)setAllowsCellularAccess:(BOOL)allowsCellularAccess {
if (_allowsCellularAccess != allowsCellularAccess)
{
[self willChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))];
// 此處增加需要執行的額外操作
_allowsCellularAccess = allowsCellularAccess;
[self didChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))];
}
}
當然利用手動監聽,你還可以實現對數組和字典這樣內部存儲數據改變是后出發通知,不過不好的是,需要你在每個數組/字典改變得地方加上如下代碼
[p willChangeValueForKey:NSStringFromSelector(@selector(array))];
[p.array addObject:@"123"];
[p didChangeValueForKey:NSStringFromSelector(@selector(array))];
也許有更好的方法,也可以給我留下言,互相交流
3.利用KVO實現某個屬性因多個屬性值得改變而改變
實現代碼:
+ (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key
{
if ([key isEqualToString:NSStringFromSelector(@selector(array))])
{
return [NSSet setWithObjects:NSStringFromSelector(@selector(age)),NSStringFromSelector(@selector(name)),nil];
}
return [super keyPathsForValuesAffectingValueForKey:key];
}
上面代碼的意思是指,當age或者name發生改變時,會觸發array的KVO通知,值得一提的NSSet
里面存儲的是keyPaths
路徑,也就是說你可以存儲xxx.ooo
這樣的路徑進行監聽
4.移除通知
在監聽的對象里面移除監聽
- (void)dealloc
{
[self removeObserver:self forKeyPath:NSStringFromSelector(@selector(age))];
[self removeObserver:self forKeyPath:NSStringFromSelector(@selector(name))];
[self removeObserver:self forKeyPath:NSStringFromSelector(@selector(array))];
}
5.注意點
對象實現了KVO
以后,系統會利用運行時向系統注冊一個新的類,例如Person
實現了KVO
,那么系統會創建這樣的一個類NSKVONotifying_Person
這樣Person類會帶來一個問題,在實現
+ (void)initialize
{
NSLog(@"-----%@",[self class]);
}
方法的時候,這個方法會被調用兩次,所以如果你同時實現了這兩個方法,需要用class進行判定,這樣代碼才不會重復調用