iOS开发攻城狮的集散地iOS

iOS开发:KVO底层实现

2018-04-11  本文已影响23人  Jason_hzb

KVO底层原理

Person * p =[[Person alloc] init];
[p addObserver:self forKeyPath:@"age" options: NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];

p这个对象一旦添加观察者后,系统会将这个Person这个类的isa指针修改为NSKVONotifying_Person,NSKVONotifying_Person 这个类是Person类的子类,这样每次访问p其实访问NSKVONotifying_Person这样类名的p,然后系统会重写p age属性的set方法.在set方法里面调用

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context

这个方法.完了再调用super 方法.

123.png

知道原理,我们就可以自己来写一个自己的KVO验证一下

一、模仿系统给NSObejct添加一个分类,具有添加自己的观察者的能力

//
//  NSObject+HZB_KVO.h
//  KVC-KVO
//
//  Created by Jason on 2018/4/11.
//  Copyright © 2018年 hzb. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface NSObject (HZB_KVO)

- (void)Hzb_addObserver:(NSObject *_Nonnull)observer forKeyPath:(NSString *_Nonnull)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;

@end
//
//  NSObject+HZB_KVO.m
//  KVC-KVO
//
//  Created by Jason on 2018/4/11.
//  Copyright © 2018年 hzb. All rights reserved.
//

#import "NSObject+HZB_KVO.h"
#import "HZB_KVONotifying_Person.h"
#import <objc/runtime.h>

@implementation NSObject (HZB_KVO)

- (void)Hzb_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context {
    // 修改isa指针(runtime) 系统的NSKVONotifying_Person这个类是动态生成的,我们直接手动创建
    object_setClass(self, [HZB_KVONotifying_Person class]);
    
    // 给对象动态添加属性.目的是保存observer,好在set方法里面拿到,调用Hzb_addObserver:forKeyPath:options:context:这个方法
    objc_setAssociatedObject(self, (__bridge const void *)(keyPath), observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end

二、重写HZB_KVONotifying_Person set方法

//
//  HZB_KVONotifying_Person.h
//  KVC-KVO
//
//  Created by Jason on 2018/4/11.
//  Copyright © 2018年 hzb. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "Person.h"

@interface HZB_KVONotifying_Person : Person

@end
//
//  HZB_KVONotifying_Person.m
//  KVC-KVO
//
//  Created by Jason on 2018/4/11.
//  Copyright © 2018年 hzb. All rights reserved.
//

#import "HZB_KVONotifying_Person.h"
#import "NSObject+HZB_KVO.h"
#import <objc/runtime.h>

@implementation HZB_KVONotifying_Person

- (void)setAge:(NSInteger)age {
    id observer = objc_getAssociatedObject(self, @"age");
    if (observer && [observer respondsToSelector:@selector(Hzb_addObserver:forKeyPath:options:context:)]) {
        [observer Hzb_addObserver:observer forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
    }
    [super setAge:age];
}

@end

三、使用一下

//
//  KVOImplementVC.h
//  KVC-KVO
//
//  Created by Jason on 2018/4/11.
//  Copyright © 2018年 hzb. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface KVOImplementVC : UIViewController

@end
//
//  KVOImplementVC.m
//  KVC-KVO
//
//  Created by Jason on 2018/4/11.
//  Copyright © 2018年 hzb. All rights reserved.
//

#import "KVOImplementVC.h"
#import "Person.h"
#import "NSObject+HZB_KVO.h"

@interface KVOImplementVC ()

@property (nonatomic,strong) Person *p;

@end

@implementation KVOImplementVC

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor whiteColor];
    
    /**********************KVO底层实现***********************/
    
    _p = [[Person alloc] init];
    [_p Hzb_addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
}

#pragma mark - KVO底层实现
- (void)Hzb_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context {
    NSLog(@"自己实现的KVO");
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    _p.age++;
}

- (void)dealloc {
    [_p removeObserver:self forKeyPath:@"age"];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

输出结果:

屏幕快照 2018-04-11 16.48.55.png

源码地址:https://github.com/hanzhanbing/KVC-KVO

上一篇下一篇

猜你喜欢

热点阅读