NSString *ObserverKey = @"SetterMethodKey";
// 根据方法名获取Key
NSString *getKeyForSetter(NSString *setter) {
NSRange range = NSMakeRange(3, setter.length - 4);
NSString *key = [setter substringWithRange:range];
NSString *letter = [[key substringToIndex:1] lowercaseString];
key = [key stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:letter];
return key;
}
// 实现一个setter和通知函数
void _MySetObjectValueAndNotify(id self, SEL selector, NSString *name) {
// 1.调用父类的方法
struct objc_super superClass = {
self,
class_getSuperclass([self class])
};
objc_msgSendSuper(&superClass, selector, name);
// 2.通知观察者
NSObject *observer = objc_getAssociatedObject(self, &ObserverKey);
NSString *selectorName = NSStringFromSelector(selector);
NSString *key = getKeyForSetter(selectorName);
objc_msgSend(observer, @selector(observeValueForKeyPath:ofObject:change:context:), key, self, @{NSKeyValueChangeNewKey: name}, nil);
}
@implementation Person
- (void)snx_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context {
// 1.创建一个子类
NSString *oldName = NSStringFromClass([self class]);
NSString *newName = [NSString stringWithFormat:@"CustomKVO_%@", oldName];
Class customClass = objc_allocateClassPair([self class], newName.UTF8String, 0);
objc_registerClassPair(customClass);
// 2.修改修改isa指针
object_setClass(self, customClass);
// 3.重写set方法
NSString *selectorName = [NSString stringWithFormat:@"set%@:", keyPath.capitalizedString];
SEL sel = NSSelectorFromString(selectorName);
class_addMethod(customClass, sel, (IMP)_MySetObjectValueAndNotify, "v@:@");
// 4.绑定观察者
objc_setAssociatedObject(self, &ObserverKey, observer, OBJC_ASSOCIATION_ASSIGN);
}
@end
重要
使用objc_msgSendSuper时,可能编译器会报错:
Too many arguments to function call, expected 0, have 3
解决办法:在Build Setting修改Enable Strict Checking of objc_msgSend Calls为No。
- (void)viewDidLoad {
[super viewDidLoad];
self.p1 = [[Person alloc] init];
self.p2 = [[Person alloc] init];
self.p1.name = @"Tom";
NSLog(@"before kvo --- p2: %s", object_getClassName(self.p2));
NSLog(@"before kvo --- p1: %p p2: %p", [self.p1 methodForSelector:@selector(setName:)], [self.p2 methodForSelector:@selector(setName:)]);
// [self.p2 addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
[self.p2 snx_addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
NSLog(@"after kvo --- p2: %s", object_getClassName(self.p2));
NSLog(@"after kvo --- p1: %p p2: %p", [self.p1 methodForSelector:@selector(setName:)], [self.p2 methodForSelector:@selector(setName:)]);
self.p2.name = @"Jerry";
}
// 输出
before kvo --- p2: Person
before kvo --- p1: 0x103514460 p2: 0x103514460
after kvo --- p2: CustomKVO_Person
after kvo --- p1: 0x103514460 p2: 0x103513f90
new name: Jerry