iOS 带block的KVO

作者: GlassHead | 来源:发表于2017-12-18 17:08 被阅读0次

    对于iOS 开发者来说,KVO(key-value-observing)的使用大家已经不再陌生,而且使用起来也是非常方便。

    KVO的简单使用:

    KVOObject *object = [[KVOObject alloc] init];
     [object addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
    object.name = @"123";
    

    这样我们就已经为object的name属性添加了监听,只要object的name属性发生改变,我们就可以通过KVO的回调方法获取其新值。

    -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
         NSLog(@"新值为 %@", [change objectForKey:NSKeyValueChangeNewKey]);
    }
    
    2017-12-18 16:44:36.096356+0800 KVO[5973:274888] 新值为 123
    

    KVO的基本使用就是这样,那么KVO是用什么原理实现的呢?如何自己实现一个带block的KVO呢?
    KVO原理:例如在为Object类添加监听时,苹果动态的为我们添加了一个类,类的名字是NSKVONotifying_Object,并且NSKVONotifying_Object是Object的子类,然后把指向Object的类指向了NSKVONotifying_Object,然后在子类中重写setter方法。

    直接上代码:

    -(void)sp_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context callBack:(void (^)(id _Nullable))block{
        NSString *className = [@"SP_" stringByAppendingString:NSStringFromClass([self class])];
        Class newClass = objc_allocateClassPair([self class], className.UTF8String, 0);//动态生成一个类,类名在原类基础上加一个前缀SP_
        objc_registerClassPair(newClass);//注册该类
        object_setClass(self, newClass);//把指针指向子类
        class_addMethod(newClass, @selector(setName:), (IMP)classSetName, "v@:@");//重写set方法
        objc_setAssociatedObject(self, &blockKey, block, OBJC_ASSOCIATION_RETAIN_NONATOMIC);//关联block对象
        
    }
    
    void classSetName(id self,SEL _cmd, NSString * newName){
        struct objc_super superClass = {
            .receiver = self,
            .super_class = class_getSuperclass(object_getClass(self))
        };
        
        // 调用父类中setter方法
        objc_msgSendSuper(&superClass,_cmd,newName);
        void(^block)(id paramter) = objc_getAssociatedObject(self, &blockKey);
        if (block) {
            block(newName);
        }
    }
    

    viewController中调用:

    @interface ViewController ()
    @property (nonatomic, strong) KVOObject * object;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        KVOObject *object = [KVOObject new];
        [object sp_addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil callBack:^(id  _Nullable paramter) {
            NSLog(@"block回调: %@\nobject name属性值: %@",paramter,object.name);
        }];
        _object = object;
    }
    
    
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        static int a = 0;
        a++;
        _object.name = [NSString stringWithFormat:@"%d",a];
    }
    
    KVO[6404:296022] block回调: 1
    object name属性值: 1
    2017-12-18 17:05:34.432499+0800 KVO[6404:296022] block回调: 2
    object name属性值: 2
    

    相关文章

      网友评论

        本文标题:iOS 带block的KVO

        本文链接:https://www.haomeiwen.com/subject/mwmmwxtx.html