KVO

KVO

Key-Value observing(KVC),键值观察,它提供一种机制,当被观察的对象的属性被修改后,KVO会自动通知相对应的观察者。接下来我会演示一下KVO的例子。

观察Model里的属性变化

废话不多说,直接上代码。现有Model: Person

@interface Person : NSObject

@property (nonatomic, copy) NSString *name;

@property (nonatomic, strong) NSNumber *age;

@end

我们来通过KVO动态监听Person中name, age的变化。这里是标准的操作流程:

第一步:注册,指定被观察者对象

[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];

[self.person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];

第二步:实现回调方法

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

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

self.titleLabel.text = self.person.name;

}

}

第三步:移除观察者,如果没有移除观察者,会引发异常,这也是KVO不方便的一点

[self.person removeObserver:self forKeyPath:@"name"];

[self.person removeObserver:self forKeyPath:@"age"];

如上:实现这三部,一个简单的KVO,我们就搞定了。是不是 so easy!

经典案例

下拉刷新,通过监听 frame 来改变动画效果

创建 TableViewController,自定义UIRefreshControl,在这里边监听 frame,来改变动画效果

[self addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil];

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

if (self.frame.origin.y > 0 ) {return;}

if (self.frame.origin.y < -60 ) {

[UIView animateWithDuration:0.5 animations:^{

self.refreshView.arrowView.transform = CGAffineTransformRotate(self.refreshView.arrowView.transform, M_PI);

self.refreshView.titleLabel.text = @"下拉刷新";

} completion:^(BOOL finished) {

}];

}else if (self.frame.origin.y >= -60){

[UIView animateWithDuration:0.5 animations:^{

self.refreshView.arrowView.transform = CGAffineTransformRotate(self.refreshView.arrowView.transform, M_PI);

self.refreshView.titleLabel.text = @"松手返回";

} completion:^(BOOL finished) {

}];

}

}

关联依赖

是不是说,掌握了第一部分,我们就能是使用KVO了。答案是肯定的,如果各位看官不想在继续了解的话,请按左上角【返回】按钮。哈哈哈,开玩笑啦,接下来给大家介绍一个好玩的例子,关联依赖。

废话不多说,直接上代码,我就是这么痛快

假设有Person类,其中有三个属性: firstName, lastName, fullName,那当我们要动态的观察 fullName时,怎么办呢。我们都知道fullName 是跟 firstName, lastName有关联的,依据上文说到的方法,这时我们要注册三个观察对象,这很low,这时,依赖关联可以登场了。

第一步: 重写 + (NSSet *)keyPathsForValuesAffection<键名>方法

// 设置 fullName 依赖 firstName, lastName

// 注册观察 fullName, 当firstName, lastName变化时,就能收到通知

+ (NSSet *)keyPathsForValuesAffectingFullName {

NSSet *set = [NSSet setWithObjects:@"firstName",@"lastName", nil];

return set;

}

第二步:之后操作同上

[self.person addObserver:self forKeyPath:@"fullName" options: NSKeyValueObservingOptionPrior context:nil];

这时,我们在改变 firstName, lastName中任意一个属性值的时候,我们通过注册的 fullName 都可以获取到,是不是很神奇啊!

手动通知 VS 自动通知

有没有感觉KVO很神奇,但实际上发生的事情是:当 name 的实例 -setName: 方法被调用的时候,系统自动在之前与之后帮我们添加了部分代码:

- (void)willChangeValueForKey:(NSString *)key

- (void)didChangeValueForKey: (NSString *)key

他们分别会在 setName: 中的代码之前与之后调用。有些时候,我们需要自己来控制是否发送通知,或者改变某些特殊的数据内容,就可以做如下操作:

第一步:重写 setter 方法,并手动添加通知前后方法

-(void)setName:(NSString *)name {

[self willChangeValueForKey:@"name"];

// 这里可以对处理特别操作

_name = [NSString stringWithFormat:@"%@123", name];

[self didChangeValueForKey:@"name"];

}

第二步: 实现 automaticallyNotifiesObserversForKey,return NO

// 通过此方法,决定是否发送通知

+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {

// 如果是 name 则,手动发送通知

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

return NO;

}

// 其他,则自动发送通知

return [super automaticallyNotifiesObserversForKey:key];

}

参数详解

addObserver:forKeyPath: options:context:

observer:观察者,一般都是 self(本身)

keyPath: 被观察的属性名称,例如上文中的 @"name", @"age"

options: 观察属性的新值、旧值等的一些配置(枚举值)

NSKeyValueObservingOptionNew    = 0x01,

NSKeyValueObservingOptionOld    = 0x02,

NSKeyValueObservingOptionInitial = 0x04,

NSKeyValueObservingOptionPrior  = 0x08

四个值的含义如下:

NSKeyValueObservingOptionNew:接收方法中使用change参数传入变化后的新值,键为:NSKeyValueChangeNewKey;

NSKeyValueObservingOptionOld:接收方法中使用change参数传入变化前的旧值,键为:NSKeyValueChangeOldKey;

NSKeyValueObservingOptionInitial:注册之后立刻调用接收方法,如果配置了NSKeyValueObservingOptionNew,change参数内容会包含新值,键为:NSKeyValueChangeNewKey;

NSKeyValueObservingOptionPrior:如果加入这个参数,接收方法会在变化前后分别调用一次,共两次,可以分别获取变化前后不通的值

context: 上下文,可以为kvo的回调方法传值

observeValueForKeyPath: ofObject: change:context:

keyPath:属性名称,通过它可以获取到当前观察的是哪个属性

object:被观察的对象

change:变化前后的值都存储在change字典中

context:注册观察者时,context传过来的值

线程

KVO 是线程同步的,发生变化的值与所观察的值在同一个线程上。

我们可以在改变被观察对象的值时,来开启一个线程,在回调方法中查看是否同属于同一个线程。

[self performSelectorInBackground:@selector(changeValue) withObject:nil];

- (void)changeValue {

self.starThreadLabel.text = [NSString stringWithFormat:@"开启线程:%@", [NSThread currentThread]];

self.person.name = @"我是子线程"

}

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

NSLog(@"当前线程为:%@",[NSThread currentThread]);

}

PS: 以上都是一家之言,望各位看官多加实验来验证,非喜勿喷。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容