博客> 重写KVO(iOS开发之黑魔法降临)
重写KVO(iOS开发之黑魔法降临)
2018-08-17 21:28 评论:0 阅读:270 iOS雯Ping
ios KVO底层

1.KVO是什么,其实就对应设计模式的观察者模式,观察者能够观察其他对象(被观察者)的属性。当被观察者的属性发生变化时,观察者就会被告知该变化。

2.适用场景:KVO能很方便的实现model和controller之间的通信。

  1. 用法

添加观察者:addObserver:forKeyPath:options:context:

实现观察相应方法:observeValueForKeyPath:ofObject:change:context:

在观察者释放之前移除对象上的监听:removeObserver:forKeyPath:

KVO方法中的参数

全称: Key Value Observing

如何使用 Runtime 来自己实现 KVO

在iOS里,可以添加观察者模式,来实现某个property更改后,通知指定的类。然后到observeValueForKeyPath:ofObject:change:context:提供处理

NSKeyValueObservingOptionInitial把初始化的值提供给处理方法,一旦注册,立马就会调用一次。通常它会带有新值,而不会带有旧值。

NSKeyValueObservingOptionPrior分2次调用。在值改变之前和值改变之后。  注:例子里的0就代表不带任何参数进去context: 可以带入一些参数,其实这个挺好用的,任何类型都可以,自己强转就好了。

处理方法里:

keyPath: 对应forKeyPath

object: 被观察的对象

change: 对应options里的NSKeyValueObservingOptionNew、NSKeyValueObservingOptionOld等

context: 对应context

自定义的 KVO 的用法 :

第一步 : 注册KVO

  • (void)secondRegisteCustomKVO

{

//

if (!self.message) {

    self.message = [[SecondMessage alloc] init];

}

NSString *key = NSStringFromSelector(@selector(text));

[self.message PG_addObserber:self forKey:key withBlock:^(id observingObject, NSString *observedKey, id oldValue, id newValue) {

    NSLog(@"%@ . %@ is now:%@",observingObject,observedKey,newValue);

    dispatch_async(dispatch_get_main_queue(), ^{

        self.textField.text = newValue;

    });

}];

[self onCustomKVOButtonClick:nil];

}

第二步 :移除KVO

  • (void)viewWillDisappear:(BOOL)animated

{

// 如果不移除掉的话会造成 内存泄漏

NSString *key = NSStringFromSelector(@selector(text));

NSLog(@"key is %@",key);

[self.message PG_removeObserver:self forKey:key];

}

但是 还是 出现了问题 如果 被观察者的属性 是 基本数据类型 例如 int ,float 等的类型。笔者 发现 原因出在 NSObject+KVO分类中的 自定义的 setter 方法

pragma mark ---- 很明显这个 setter 方法 和getter 方法 只是 写了id 类型 的没写 基础类型的 比如int float 等 笔者要加上这些类型 Thread 1: EXC_BAD_ACCESS (code=1, address=0x2)

static void kvo_setter(id self,SEL _cmd,id newValue)

实例:

1.创建model

ObserveObject.h:@interfaceObserveObject:NSObject

@property(nonatomic,strong)NSString*name;

@property(nonatomic)NSIntegerage;

@end

2 .创建被观察者

ObserveObject *observe = [[ObserveObject alloc] init];

observe.name = @"tom";

observe.age = 20;

3 .添加观察者

[observe addObserver:selfforKeyPath:@"name"options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNewcontext:nil];

4 .实现监听方法

  • (void)observeValueForKeyPath:(NSString)keyPath ofObject:(id)object change:(NSDictionary )change context:(void*)context {

if([keyPath isEqualToString:@"name"]) {

NSString*oldName = change[NSKeyValueChangeOldKey];

NSString*newName = change[NSKeyValueChangeNewKey];

NSLog(@"old name = %@, new name = %@",oldName,newName); }}

5 .移除监听

-(void)removeObserver:(NSObject)observerforKeyPath:(NSString)keyPathcontext:(nullablevoid*)contextAPI_AVAILABLE(macos(10.7),ios(5.0),watchos(2.0),tvos(9.0));

一般在dealloc方法中调用。这里有一个问题需要了解下,注册和移除是一一对应的,有几个注册就要有几个移除。如果一个类中有多个监听对象,可以有context值来区分。

收藏
0
sina weixin mail 回到顶部