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 这个属性的。