您的位置:首页 > 移动开发 > IOS开发

iOS进阶之runtime(2)--KVO简单模拟实现

2015-11-29 11:20 465 查看
前言:

上篇简单介绍了下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];
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: