iOS进阶之runtime(2)--KVO简单模拟实现
前言:
上篇简单介绍了下runtime的几个API和KVO的底层原理,现在开始进入正题,来利用这几个API简单的模拟下KVO的原理.大概的步骤就是.
1.在注册KVO的时候注册一个通知,并且替换set方法.
2.然后在我们的set方法里调用原有的set方法,好继续执行原有set方法的逻辑,比如赋值等.
3.然后在我们替换的set方法里判断值是否发生变化,如果发生变化就发送一个通知执行我们特定的观察者方法.
好了不多说,直接贴代码. demo下载请戳我
- (void)zxp_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context { //进行KVO监听的时候,注册一个通知 [[NSNotificationCenter defaultCenter] addObserver:observer selector:@selector(sendNotification:) name:[self notificationNameWithkeyPath:keyPath] object:nil];
//设置关联,,把需要监听的KVO属性(path)保存起来 objc_setAssociatedObject(self, @selector(getKeyPathString), keyPath, OBJC_ASSOCIATION_COPY_NONATOMIC); objc_setAssociatedObject(self, @selector(getKeyValueOptions), @(options), OBJC_ASSOCIATION_ASSIGN); //得要这个监听属性的set方法 NSString *setMethodString = [NSString stringWithFormat:@"set%@%@:",[keyPath substringToIndex:1].uppercaseString,[keyPath substringFromIndex:1]]; //把 原有的set方法替换成我们的set方法 method_exchangeImplementations(class_getInstanceMethod([self class], NSSelectorFromString(setMethodString)), class_getInstanceMethod([NSObject class], @selector(newSetMethod:))); } //移除观察者 - (void)zxp_removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath { [[NSNotificationCenter defaultCenter] removeObserver:observer name:[self notificationNameWithkeyPath:keyPath] object:observer]; } //观察者的值发生变化的时候执行,需要监听的对象重写此方法,否则抛出异常 - (void)zxp_observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context { NSAssert(NO, @"需要实现方法:%@",NSStringFromSelector(@selector(zxp_observeValueForKeyPath:ofObject:change:context:))); } #pragma mark - private - (NSString *)notificationNameWithkeyPath:(NSString *)keyPath { return [NSString stringWithFormat:@"%zi,%@,%@",self,NSStringFromSelector(@selector(notificationNameWithkeyPath:)),keyPath]; } //我们自己实现的set方法,在set方法里判断监听的属性是否发生变化,如果发生变化就发送一个通知 - (void)newSetMethod:(id)params { id value = [self valueForKeyPath:[self getKeyPathString]]; if (value != params && [self getKeyValueOptions] == NSKeyValueObservingOptionNew) { [[NSNotificationCenter defaultCenter] postNotificationName:[self notificationNameWithkeyPath:[self getKeyPathString]] object:nil]; } [self newSetMethod:params]; } - (NSString *)getKeyPathString { return objc_getAssociatedObject(self, @selector(getKeyPathString)); } - (NSKeyValueObservingOptions)getKeyValueOptions { return [objc_getAssociatedObject(self, @selector(getKeyValueOptions)) integerValue]; } #pragma mark - notification methods //通知的方法,如果收到此通知就执行zxp_observeValueForKeyPath:ofObject: change: context: 方法
- (void)sendNotification:(NSNotification *)notification { #warning 没做传参,随便用什么保存这几个参数存进来就行了,比如AssociatedObject里 [self zxp_observeValueForKeyPath:nil ofObject:nil change:nil context:nil]; }
作者:张孝平
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。