iOS开发

iOS 反 KVO(如何知道属性被监听了)

2018-01-30  本文已影响1050人  chenyu1520

在 iOS 开发中监听一个对象的某个属性,很容易做到,然后有时候我们在写一些安全性的代码或者框架时,不想让别人监听我的某个属性怎么做呢?

有一个做法,hook 掉系统的 addObserver 方法,在这里判断 keyPath 是否是不想让监听的属性。

具体代码如下:

#import <objc/runtime.h>

@implementation NSObject (CYKVO)

+ (void)load
{
    [self switchMethod];
}

// 交换后的方法
- (void)cy_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context
{
    @try {
        if ([keyPath isEqualToString:@"can_not_observer_name"]) {
            return;
        }
        [self cy_addObserver:observer forKeyPath:keyPath options:options context:context];
    } @catch (NSException *exception) {}
}

+ (void)switchMethod
{
    SEL cyAddSel = @selector(cy_addObserver:forKeyPath:options:context:);
    SEL sysAddSel = @selector(addObserver:forKeyPath:options:context:);
    
    Method cyAddMethod = class_getClassMethod([self class],cyAddSel);
    Method sysAddMethod = class_getClassMethod([self class], sysAddSel);
    
    method_exchangeImplementations(cyAddMethod, sysAddMethod);
}

@end

具体使用场景可能是这样的:
有一个类是这样写的:

//.h
@interface Person : NSObject

@property (nonatomic, strong) NSString *can_not_observer_name;

@end

//.m
@implementation Person

-(instancetype)init {
    self = [super init];
    self.can_not_observer_name = @"name";
    
    return self;
}

@end

在某个 VC 里这样使用:

@interface ViewController ()
@property (nonatomic, strong)Person *person;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.person = [[Person alloc] init];
    [self.person addObserver:self forKeyPath:@"can_not_observer_name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    NSLog(@"observeValueForKeyPath");
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    self.person.can_not_observer_name = @"set a new value";
}

@end

这样是监听不到 can_not_observer_name 这个属性的。

上一篇下一篇

猜你喜欢

热点阅读