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. .netCore2.0 WebApi 传递form表单

    随着it的技术发展,目前越来越多的项目采用前后端分离的开发模式,通过webapi提供接口数据来进行交互 最近项目用的是.netCore WebApi,在最近的项目使用中发现一些问题,进行记录.个人简介 ...

  2. in和not in

    当子查询返回的列的值是多个值,那么就不能使用比较运算符(> < = !=),使用关键字in 语法: select …..from …..where 表达式 in (子查询) 常用in替换等 ...

  3. css样式margin padding border

  4. K2.ActivityInstanceDestination.User is NULL

    his one caught me by surprise! I needed to build up a CSV list of email addresses for all previous t ...

  5. 解决javac无效的目标发行版1.8问题

    之前遇到了几次这个问题,解决了又忘记了,所以特别记录一下这个问题. 遇到这个问题,改pom文件不行,改project的sdk也不行,后面看到网上说真正的原因是maven的runner的jre的环境依然 ...

  6. hdu 1565 方格取数(1) 状态压缩dp

    方格取数(1) Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Su ...

  7. 动态设置热区coords的坐标

    window.onresize = adjuest; function adjuest(){ var picw = $(".imgbox img").width(); var pi ...

  8. Web前端面试指导(十七):一个满屏 品 字布局 如何设计?

    题目点评 这道题目有可能是笔试题,有可能面谈的时候进行叙述,如果是笔试题要求对css样式代码非常熟练,如果是面谈叙述,就需要你的表达能力非常强,要抓住要点,把需要用到的技能点讲清楚就可以了. 需要用到 ...

  9. 如何创建一个基本JQuery的插件

    如何创建一个基本的插件 有时您希望在整个代码中提供一些功能.例如,也许你想要一个单一的方法,你可以调用一个jQuery选择,对选择执行一系列的操作.在这种情况下,您可能需要编写一个插件. 链接jQue ...

  10. VC学习笔记----STL库

      STL = Standard Template Library,标准模板库,惠普实验室开发的一系列软件的统称.它是由Alexander Stepanov.Meng Lee和David R Muss ...