KVO原理:調用監(jiān)聽對象屬性的方法吝沫,
動態(tài)
創(chuàng)建一個繼承自該對象所屬類的子類掷匠,然后重寫該屬性的setter方法,在setter方法類調用willChangeValueForKey:
和didChangeValueForKey:
方法來觸發(fā)observeValueForKeyPath:ofObject:change:context:
方法据忘。
大概看了一下峡钓,大多數(shù)手動實現(xiàn)KVO都并沒有做到真正動態(tài)創(chuàng)建類,而是手動若河。本文主要是動態(tài)創(chuàng)建繼承類
及動態(tài)重寫setter方法
能岩。
直接貼代碼吧,注釋都在代碼里面萧福。
首先創(chuàng)建NSObject分類NSObject+XKVO
暴露接口:
.h
#import <Foundation/Foundation.h>
@interface NSObject (XKVO)
- (void)xl_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context ;
@end
.m
#import "NSObject+XKVO.h"
#import <objc/runtime.h>
#import <objc/message.h>
@implementation NSObject (XKVO)
// 屬性:用于保存監(jiān)聽對象
static char *kObserver = "kObserver";
- (void)setObserver:(id)observer {
objc_setAssociatedObject(self, kObserver, observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)observer {
return objc_getAssociatedObject(self, kObserver);
}
void setterMethod(id self, SEL _cmd, id obj) {
/* 根據(jù)_cmd和屬性列表獲取屬性名和屬性對應的old value數(shù)據(jù) */
NSString *name = NSStringFromSelector(_cmd);
NSString *keyPath = [[name lowercaseString] substringFromIndex:3];
keyPath = [keyPath substringToIndex:keyPath.length-1];
id oldValue ;
unsigned int count ;
Ivar *ivarlist = class_copyIvarList([self superclass], &count);
for (int i = 0; i < count; i++) {
Ivar thisivar = ivarlist[i];
NSString *name = [NSString stringWithUTF8String:ivar_getName(thisivar)];
NSString *properName = [name substringFromIndex:1];
name = [[name lowercaseString] substringFromIndex:1];
BOOL isEqual = [name isEqualToString:keyPath];
if (isEqual) {
keyPath = properName;
oldValue = [self valueForKey:keyPath];
break;
}
}
/* 調用父類的setter方法拉鹃,保證設置數(shù)據(jù)不被干擾 */
Method sm = class_getInstanceMethod([self superclass], _cmd);
IMP imp = method_getImplementation(sm);
imp(self, _cmd, obj);
id obs = objc_getAssociatedObject(self, kObserver);
if (obs) {
/* 調用監(jiān)聽對象實現(xiàn)的方法 */
objc_msgSend(obs, @selector(observeValueForKeyPath:ofObject:change:context:), keyPath, self, @{@"NSKeyValueObservingOptionNew":obj, @"NSKeyValueObservingOptionOld":oldValue}, nil);
}
}
- (void)xl_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context {
/* 保存監(jiān)聽對象 */
self.observer = observer;
/* 動態(tài)創(chuàng)建子類 */
Class NSObjectKVO = objc_allocateClassPair([self class], "NSObjectKVO", 0);
/* 獲取setter方法名 */
NSString*setterName=[NSString stringWithFormat:@"set%@%@:",[[keyPath substringToIndex:1] uppercaseString],[keyPath substringFromIndex:1]];
/* 為子類動態(tài)添加方法 */
BOOL success = class_addMethod(NSObjectKVO, NSSelectorFromString(setterName), (IMP)setterMethod, "V@:");
if (success) {
/* 將當前對象指向新建的子類 */
object_setClass(self, NSObjectKVO);
}else{
}
}
@end