KVO

KVO属于设计模式中的观察者模式,在观察者模式中,一个对象任何状态的变更都会通知另外的对改变感兴趣的对象。这些对象之间不需要知道彼此的存在,这其实是一种松耦合的设计。当某个属性变化的时候,我们通常使用这个模式去通知其它对象。

本人用3种方式来讲述KVO的使用,开始前新建一个对象Student类,用以监控Student类中name属性,源码如下

Student.h + Student.m

#import <Foundation/Foundation.h>

@interface Student : NSObject

@property (nonatomic, strong) NSString *name;

@end
#import "Student.h"

@implementation Student

@end

注:所有测试均在ARC环境下

1. 使用系统自带的KVO来测试

延时执行GCD函数

// 系统并发线程池中延时多少ms的block函数
void delayMicroSeconds(int64_t microSeconds, void(^block)())
{
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, microSeconds * USEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
block();
});
}

实现细节

- (void)viewDidLoad
{
[super viewDidLoad]; // 实例化对象
Student *stu = [[Student alloc] init];
stu.name = @"Y.X."; // 添加观察者
[stu addObserver:self
forKeyPath:@"name"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
context:nil]; // 延时1000ms后改变stu的name属性值
delayMicroSeconds(, ^{
stu.name = @"Jane";
});
}

监控的方法

// 监听的函数
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
NSLog(@"%@", change);
}

执行后的打印信息如下

-------------------------------------------------------------------------------------------------------------------------------------------------------------

2014-03-25 17:25:29.316 StudyKVOVer2[4342:60b] {
    kind = 1;
    new = Jane;
    old = "Y.X.";
}
2014-03-25 17:25:29.318 StudyKVOVer2[4342:60b] An instance 0x8c70030 of class Student was deallocated while key value observers were still registered with it. Observation info was leaked, and may even become mistakenly attached to some other object. Set a breakpoint on NSKVODeallocateBreak to stop here in the debugger. Here's the current observation info:
<NSKeyValueObservationInfo 0x8c70110> (
<NSKeyValueObservance 0x8c70200: Observer: 0x8c6d0c0, Key path: name, Options: <New: YES, Old: YES, Prior: NO> Context: 0x0, Property: 0x8c700f0>
)

-------------------------------------------------------------------------------------------------------------------------------------------------------------

请注意,因为在ARC环境下,Student默认是weak属性,出了viewDidLoad方法后直接被回收了,如上面红字部分所描述的.

使用就如上面那样,很容易,但不是block实现的

2. 使用开源库 THObserversAndBinders 实现KVO

下载源码 https://github.com/th-in-gs/THObserversAndBinders

将文件夹 THObserversAndBinders 拖入到工程文件中,引入相关的头文件

将THObserver转化为强引用(必须的一步)

@interface RootViewController ()

{
THObserver *_observer;
} @end

实现部分

- (void)viewDidLoad
{
[super viewDidLoad]; // 实例化对象
Student *stu = [[Student alloc] init];
stu.name = @"Y.X."; // 创建监听者
_observer = [THObserver observerForObject:stu keyPath:@"name" oldAndNewBlock:^(id oldValue, id newValue) {
NSLog(@"stu changed, was %@, is now %@", oldValue, newValue);
}]; // 延时1000ms后改变stu的name属性
delayMicroSeconds(, ^{
stu.name = @"Jane";
});
}

打印信息:

-------------------------------------------------------------------------------------------------------------------------------------------------------------

2014-03-25 17:37:04.269 StudyKVOVer2[4509:60b] stu changed, was Y.X., is now Jane
2014-03-25 17:37:04.272 StudyKVOVer2[4509:60b] An instance 0xa365ee0 of class Student was deallocated while key value observers were still registered with it. Observation info was leaked, and may even become mistakenly attached to some other object. Set a breakpoint on NSKVODeallocateBreak to stop here in the debugger. Here's the current observation info:
<NSKeyValueObservationInfo 0xa366990> (
<NSKeyValueObservance 0xa366a80: Observer: 0xa3668b0, Key path: name, Options: <New: YES, Old: YES, Prior: NO> Context: 0x1, Property: 0xa366970>
)

-------------------------------------------------------------------------------------------------------------------------------------------------------------

Student还是被回收了,与第一个例子是一致的

很明显,这次使用起来简单了不少,该开源库本身有着详细的使用样例,但可能会照成循环引用,引用原文如下

This stuff seems to be making a lot of retain cycles and leaks...

I suspect you're doing something like this:

_observerIvar = [THObserver observerForObject:_objectIvar keyPath:@"propertyToObserve" block:^{
NSLog(@"propertyToObserve changed, is now %@", _objectIvar.propertyToObserve);
}];

This will create a retain cycle. The reference of _objectIvar inside the block will cause the block - and hence the observer - to strongly retain self. The observer is in turn retained by self when you assign it to _observerIvar, creating the cycle (self retains _observerIvar, which retains the block, which retains self).

You can instead do something like this:

MyObject *blockObject = _objectIvar;
_observerIvar = [THObserver observerForObject:blockObject keyPath:@"propertyToObserve" block:^{
NSLog(@"propertyToObserve changed, is now %@", blockObject.propertyToObserve);
}];

or:

__weak MySelf *weakSelf = self;
_observerIvar = [THObserver observerForObject:self.objectProperty keyPath:@"propertyToObserve" block:^{
NSLog(@"propertyToObserve changed, is now %@", weakSelf.objectProperty.propertyToObserve);
}];

And remember to ensure that the observer is not observing by the time that the object in _objectIvar is released (e.g. by calling [_observerIvar stopObserving] in your dealloc).

(Thanks to Peter Steinberger for pointing out that this could use elucidation.)

3. 使用开源库 FBKVOController 实现KVO

下载源码 https://github.com/facebook/KVOController

将 FBKVOController.h FBKVOController.m 拖到工程中引入头文件即可

Key-value observing is a particularly useful technique for communicating between layers in a Model-View-Controller application. KVOController builds on Cocoa's time-tested key-value observing implementation. It offers a simple, modern API, that is also thread safe. Benefits include:

KVO在MVC架构的应用中,在其层级之间的交互上十分有用,KVOController是在Cocoa上KVO实现的,他提供了一个简单地API接口,而且是线程安全的,好处如下所示:

  • Notification using blocks, custom actions, or NSKeyValueObserving callback.
  • No exceptions on observer removal.
  • Implicit observer removal on controller dealloc.
  • Improved performance when using NSKeyValueObservingInitial.
  • Thread-safety with special guards against observer resurrection – rdar://15985376.
  • 监听可以使用blocks,自定义actions或者NSKeyValueObserving回调
  • 在移除监听时不会出现异常
  • 当controller释放时该监听才被移除
  • 提升了一些效果,当在使用NSKeyValueObservingInitial
  • 线程安全

For more information on KVO, see Apple's Introduction to Key-Value Observing.

使用细节:

@interface RootViewController ()

{
FBKVOController *_KVOController;
} @property (nonatomic, strong) Student *stu; @end
- (void)viewDidLoad
{
[super viewDidLoad]; // 初始化对象
_stu = [[Student alloc] init];
_stu.name = @"Y.X."; // 初始化监听者
_KVOController = [FBKVOController controllerWithObserver:self]; // 开始监听
[_KVOController observe:_stu
keyPath:@"name"
options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew
block:^(id observer, id object, NSDictionary *change) {
NSLog(@"%@", change);
}]; // 延时1000ms后改变stu的属性
delayMicroSeconds(, ^{
_stu.name = @"Jane";
});
}

其打印信息:

-------------------------------------------------------------------------------------------------------------------------------------------------------------

2014-03-25 17:53:42.806 StudyKVOVer2[4737:60b] {
    kind = 1;
    new = Jane;
    old = "Y.X.";
}

-------------------------------------------------------------------------------------------------------------------------------------------------------------

本次将 Student 对象设置成强引用,所以没有出现上面出现的问题,注意,经测试,FBKVOController也必须是强引用

心得:

1. KVO系统的没有block实现的方式,还要注意什么时候释放,不怎么好用

2. 使用开源库的KVO可以使用block,方便

3. 推荐使用 FBKVOController 的KVO实现,简单且线程安全

4. ARC和非ARC有很大区别,本人开始时由于没有想到弱引用问题而无法看到想要的现象,也是学习的一个过程

3种方式实现KVO并进行对比的更多相关文章

  1. JAVA主动抛异常的几种方式及捕捉结果输出对比

    测试代码: /** * 测试异常抛出及捕捉 */ @Test public void test() { try { this.testA(); } catch (Exception ex) { Sys ...

  2. Delegate、Thread、Task、ThreadPool几种方式创建异步任务性能对比

    开始预测的结果是 Task>Delegate>ThreadPool>>Thread. (一)测试代码 static async Task<int> AsyncTas ...

  3. Dynamics CRM2016 查询数据的三种方式的性能对比

    之前写过一个博客,对非声明验证方式下连接组织服务的两种方式的性能进行了对比,但当时只是对比了实例化组织服务的时间,并没有对查询数据的时间进行对比,那有朋友也在我的博客中留言了反映了查询的时间问题,一直 ...

  4. CSS控制样式的三种方式优先级对比验证

    入职已经一个月了,自此后,就好久没有写过博客了,在此先跟关注我的博友们说声抱歉.今天,在公司的一个培训作业的驱动以及伟哥那句“再不写博客就开除你”的监督下,我终于重拾旧爱,再次登录博客园,继续与大家分 ...

  5. iOS中保证线程安全的几种方式与性能对比

    来源:景铭巴巴 链接:http://www.jianshu.com/p/938d68ed832c 一.前言 前段时间看了几个开源项目,发现他们保持线程同步的方式各不相同,有@synchronized. ...

  6. Windows上安装配置SSH教程(7)——几种方式对比

    服务端:Windows XP 客户端:Windows 10 由于Cygwin也可以安装OpenSSH,所以客户端其实可以直接使用Cygwin安装OpenSSH,那么在Windows下使用SCP(安全拷 ...

  7. Linux上删除大量文件几种方式对比

    目录 Linux上删除大量文件几种方式对比 1. rm删除:因为文件数量太多,rm无法删除(报错) 2. find查找删除:-exec 3. find查找删除:xargs 4. find调用-dele ...

  8. 【转】Spring学习---Bean配置的三种方式(XML、注解、Java类)介绍与对比

    [原文]https://www.toutiao.com/i6594205115605844493/ Spring学习Bean配置的三种方式(XML.注解.Java类)介绍与对比 本文将详细介绍Spri ...

  9. Android自动化测试中AccessibilityService获取控件信息(2)-三种方式对比

    Android自动化测试中AccessibilityService获取控件信息(2)-三种方式对比   上一篇文章: Android自动化测试中AccessibilityService获取控件信息(1 ...

随机推荐

  1. Phoenix的安装使用与SQL查询HBase

    一. Phoenix的简介 1. 什么是phoenix 现有hbase的查询工具有很多如:Hive,Tez,Impala,Shark/Spark,Phoenix等.今天主要说Phoenix.phoen ...

  2. Codeforces 429C Guess the Tree(状压DP+贪心)

    吐槽:这道题真心坑...做了一整天,我太蒻了... 题意 构造一棵 $ n $ 个节点的树,要求满足以下条件: 每个非叶子节点至少包含2个儿子: 以节点 $ i $ 为根的子树中必须包含 $ c_i ...

  3. day6 shelve模块

        shelve模块 shelve模块是一个简单的k,v将内存数据通过文件持久化的模块,可以持久化任何pickle可支持的python数据格式,shelve模块是对pickle模块的补充.我们知道 ...

  4. C语言:输入一个多位的数字,12345,求各位相加1+2+3+4+5=15

    题目: 输入一个多位的数字,12345,求各位相加1+2+3+4+5=15(10分)题目内容: 输入一个多位的数字,1求各数位相加. 例如输入12345,则计算1+2+3+4+5=15 输入格式: 一 ...

  5. css盒子垂直居中

    首先父盒子包住子盒子 <body> <div class="outbox"> <div class="box"></d ...

  6. ref:关于JAVA中一些安全漏洞示例说明及如何规避方法代码示例总结分享

    ref:http://www.xwood.net/_site_domain_/_root/5870/5874/t_c268166.html 标签:安全,漏洞,健壮,java,SQL注入,SS及CSRF ...

  7. 安卓代码混淆(Android Studio)

    Proguard是安卓提供的方便开发者对代码和apk进行保护和精简的工具,可在SDK/tools文件夹下找到.   proguard的作用 : 1,代码混淆 2,精简代码,删掉没有用到的代码,减小ap ...

  8. win10 远程桌面远程电脑时每次要输入密码及身份验证错误,要求的函数不受支持问题解决

    解决以前每次远程时能能记住密码,更新系统补丁后现在每次登录要输入密码了及远程时提示身份验证错误,要求的函数不受支持问题 解决方法一.卸载更新安装的新补丁,远程桌面正常,能记住密码 解决方法二.修改注册 ...

  9. cloudstack模板

    玩cloudstack的人都应该玩过模板这个功能,这里还是比较有意思的,我们底层连接vcenter 创建vm采用模板 实际这里的磁盘方案,并不是给系统重新分配的磁盘大小而是又新挂上了一块磁盘,新磁盘的 ...

  10. DMA

    DMA:如果将一串字符串通过串口传送到外设中去,用传统的方法,则CPU将不断的去扫描UTSTAT这个寄存器,在字符发送期间,CPU将不能做任何其他事情.为了解决这个问题,则在诞生了DMA CPU只需要 ...