KVO的使用方法

  • 注冊
[object addObserver:observer forKeyPath:@"text" options:NSKeyValueObservingOptionNew context:nil];
  • 实现回调方法
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if([keyPath isEqualToString:@"text"])
{
NSLog(@"text:@%@", change[NSKeyValueChangeNewKey]);
}
}
  • 释放的时候取消注冊
[object removeObserver:self forKeyPath:@"text"];

这里有几个问题

  1. 注冊的时候參数过多
  2. 释放的时候必须取消注冊
  3. 仅仅有一个回调,当注冊的观察者过多的时候,会使得代码变得杂乱

KVO的封装

以下我们将针对这几个问题进行封装

  • 定义一个观察者类
@interface XYObserver : NSObject
@end @interface XYObserver () @property (nonatomic, assign) XYObserverType type; // 观察者的类型 @property (nonatomic, weak) id target; // 被观察的对象的值改变时后的响应方法所在的对象
@property (nonatomic, assign) SEL selector; // 被观察的对象的值改变时后的响应方法
@property (nonatomic, copy) XYObserver_block_sourceObject_new_old block; // 值改变时运行的block @property (nonatomic, assign) id sourceObject; // 被观察的对象
@property (nonatomic, strong) NSString *keyPath; // 被观察的对象的keyPath -(instancetype) initWithSourceObject:(id)sourceObject keyPath:(NSString*)keyPath target:(id)target selector:(SEL)selector type:(XYObserverType)type; -(instancetype) initWithSourceObject:(id)sourceObject keyPath:(NSString*)keyPath block:(XYObserver_block_sourceObject_new_old)block; @end
  • 加入NSObject关于观察者的类别
@interface NSObject (XYObserver)

@property (nonatomic, readonly, strong) NSMutableDictionary *observers;

/**
* api parameters 说明
*
* sourceObject 被观察的对象 * keyPath 被观察的属性keypath * target 默认是self * selector @selector(propertyNew:)
@selector(propertyNew:old:)
@selector(propertyIn:new:)
@selector(propertyIn:new:old:) * type 依据selector自己主动赋值 * block selector, block二选一
*/
-(void) observeWithObject:(id)sourceObject property:(NSString*)property;
-(void) observeWithObject:(id)sourceObject property:(NSString*)property block:(XYObserver_block_sourceObject_new_old)block; -(void) removeObserverWithObject:(id)sourceObject property:(NSString *)property;
-(void) removeObserverWithObject:(id)sourceObject;
-(void) removeAllObserver; @end
  • 在这里我们查询的实现的方法
-(void) observeWithObject:(id)object property:(NSString*)property{
SEL aSel = NSSelectorFromString([NSString stringWithFormat:@"%@New:", property]);
if ([self respondsToSelector:aSel]) {
[self observeWithObject:object
keyPath:property
target:self
selector:aSel
type:XYObserverType_new];
return;
}
.
.
.
}
  • 用block的话就直接保存
-(void) observeWithObject:(id)object property:(NSString*)property block:(XYObserver_block_sourceObject_new_old)block{
[self observeWithObject:object keyPath:property block:block];
}
  • 处理实现方法
-(void) observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context
{
__weak __typeof(self) weakSelf = self;
if (_block) {
_block(weakSelf, change[NSKeyValueChangeNewKey], change[NSKeyValueChangeOldKey]);
return;
} if (_type == XYObserverType_new) {
action(_target, _selector, change[NSKeyValueChangeNewKey]);
}else if (_type == XYObserverType_new_old) {
action(_target, _selector, change[NSKeyValueChangeNewKey], change[NSKeyValueChangeOldKey]);
}else if (_type == XYObserverType_self_new) {
action(_target, _selector, self, change[NSKeyValueChangeNewKey]);
}else if (_type == XYObserverType_self_new_old) {
action(_target, _selector, self, change[NSKeyValueChangeNewKey], change[NSKeyValueChangeOldKey]);
}
}
  • 把全部的观察者加入到一个字典里
-(void) observeWithObject:(id)object keyPath:(NSString*)keyPath target:(id)target selector:(SEL)selector type:(XYObserverType)type{
XYObserver *ob = [[XYObserver alloc] initWithSourceObject:object keyPath:keyPath target:target selector:selector type:type]; NSString *key = [NSString stringWithFormat:@"%@_%@", object, keyPath];
[self.observers setObject:ob forKey:key];
} -(void) observeWithObject:(id)object property:(NSString*)property block:(XYObserver_block_sourceObject_new_old)block{
[self observeWithObject:object keyPath:property block:block];
} -(id) observers{
id object = objc_getAssociatedObject(self, NSObject_observers); if (nil == object) {
NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithCapacity:8];
objc_setAssociatedObject(self, NSObject_observers, dic, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
return dic;
} return object;
}
  • 当对象释放的时候会清空字典里的观察者对象,在观察者对象的dealloc方法里面取消注冊观察者
-(void) dealloc
{
if (_sourceObject) { [_sourceObject removeObserver:self forKeyPath:_keyPath]; }
}
  • 为了方便书写,定义几个宏
#define ON_KVO_1_( __property )     -(void) __property##New:(id)newValue
#define ON_KVO_2_( __property ) -(void) __property##New:(id)newValue old:(id)oldValue
#define ON_KVO_3_( __property ) -(void) __property##In:(id)sourceObject new:(id)newValue
#define ON_KVO_4_( __property ) -(void) __property##In:(id)sourceObject new:(id)newValue old:(id)oldValue

使用的demo

[self observeWithObject:self property:@"testKVO"];

ON_KVO_4_(testKVO){
NSLogD(@"obj:%@ new:%@ old:%@", sourceObject, newValue, oldValue);
} [self observeWithObject:self property:@"testKVO2" block:^(id sourceObject, id newValue, id oldValue) {
NSLogD(@"obj:%@ new:%@ old:%@", sourceObject, newValue, oldValue);
}];

这个封装的长处是在使用KVO的时候不须要记住太多东西..

代码能够在https://github.com/uxyheaven/XYQuickDevelop下载到

iOS的KVO使用和轻量级封装的更多相关文章

  1. IOS中KVO模式的解析与应用

    IOS中KVO模式的解析与应用 最近老翁在项目中多处用到了KVO,深感这种模式的好处.现总结如下: 一.概述 KVO,即:Key-Value Observing,它提供一种机制,当指定的对象的属性被修 ...

  2. iOS下KVO使用过程中的陷阱 (转发)

    iOS下KVO使用过程中的陷阱   KVO,全称为Key-Value Observing,是iOS中的一种设计模式,用于检测对象的某些属性的实时变化情况并作出响应.网上广为流传普及的一个例子是利用KV ...

  3. 轻量级封装DbUtils&Mybatis之三MyBatis分页

    MyBatis假分页 参考DefaultResultSetHandler的skipRows方法. 温馨提示:部分代码请参考轻量级封装DbUtils&Mybatis之一概要 解决方案 1)之前公 ...

  4. 【原】iOS下KVO使用过程中的陷阱

    KVO,全称为Key-Value Observing,是iOS中的一种设计模式,用于检测对象的某些属性的实时变化情况并作出响应.网上广为流传普及的一个例子是利用KVO检测股票价格的变动,例如这里.这个 ...

  5. iOS开发——post异步网络请求封装

    IOS中有许多网络请求的函数,同步的,异步的,通过delegate异步回调的. 在做一个项目的时候,上网看了很多别人的例子,发现都没有一个简单的,方便的异步请求的封装例子.我这里要给出的封装代码,是异 ...

  6. 【原】IOS中KVO模式的解析与应用

    最近老翁在项目中多处用到了KVO,深感这种模式的好处.现总结如下: 一.概述 KVO,即:Key-Value Observing,它提供一种机制,当指定的对象的属性被修改后,则对象就会接受到通知.简单 ...

  7. iOS之KVO和KVC

    概述 由于ObjC主要基于Smalltalk进行设计,因此它有很多类似于Ruby.Python的动态特性,例如动态类型.动态加载.动态绑定等.今天我们着重介绍ObjC中的键值编码(KVC).键值监听( ...

  8. 轻量级封装DbUtils&Mybatis之二Dbutils

    DbUtils入门 Apache出品的极为轻量级的Jdbc访问框架,核心类只有两个:QueryRunner和ResultSetHandler. 各类ResultSetHandler: ArrayHan ...

  9. 轻量级封装DbUtils&Mybatis之一概要

    Why 一时兴起,自以为是的对Jdbc访问框架做了一个简单的摸底,近期主要采用Mybatis,之前也有不少采用Dbutils,因此希望能让这两个框架折腾的更好用. DbUtils:非常简单的Jdbc访 ...

随机推荐

  1. Scrapy框架学习(四)爬取360摄影美图

    我们要爬取的网站为http://image.so.com/z?ch=photography,打开开发者工具,页面往下拉,观察到出现了如图所示Ajax请求, 其中list就是图片的详细信息,接着观察到每 ...

  2. Docker 教程

    转自:http://www.runoob.com/docker/docker-tutorial.html Docker 教程

  3. WPF简单的数据库查询

    做一个简单WPF连接数据库的 控件类型和名称:DataGrid:dataGrid          Button1  :Button1              Button:   Button2   ...

  4. javascript下的arguments,caller,callee,call,apply示例及理解

    (参考:http://justcoding.iteye.com/blog/589111) Arguments  该对象代表正在执行的函数和调用它的函数的参数. [function.]arguments ...

  5. Visual Studio Code 的使用

    常用快捷键 常用General 按 Press 功能 Function Ctrl + Shift + P,F1 显示命令面板 Show Command Palette Ctrl + P 快速打开 Qu ...

  6. jQuery extend方法详解

    先说个概念的东西: jQuery为开发插件提拱了两个方法,分别是: $.fn.extend(item):为每一个实例添加一个实例方法item.($("#btn1") 会生成一个 j ...

  7. js判断值是否是数字

    js如何判断值是否是数字 1. isNaN()方法2. 正则表达式var re = /^[0-9]+.?[0-9]*$/; //判断字符串是否为数字 //判断正整数 /^[1-9]+[0-9]*]*$ ...

  8. ThreadLocal 理解

    主要方法 public void set(T value); public T get(); private T setInitialValue(); public void set(T value) ...

  9. EasyUI combobox 多选及回显赋值

    multiple boolean 决定是否支持多项选择. $('#cc').combobox({ url:'combobox_data.json', multiple:true, //支持多选 val ...

  10. GetVersion和GetVersionEx

    原文:http://www.cnblogs.com/VOIP/archive/2011/03/22/1990927.html Windows API 中有两个函数可以得到系统版本信息:GetVersi ...