KVO

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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值