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