KVC和KVO

目录:

1.KVC用法;

2.KVC和对象的setter、getter方法的区别;

3.key和keyPath的区别;

4.KVC进行求和,求平均值等操作;

5.KVO的用法;

6.根据KVO底层原理自己实现KVO

一.KVC


1.KVC用法(很简单,不详细介绍)

KVC也就是key-value-coding(键值编码),简而言之就是通过key值去进行赋值和取值。主要是是操作对象的属性。以下是几个常用的方法:

setValue:forKey:(为对象的属性赋值)

setValue: forKeyPath:(为对象的属性赋值(包含了setValue:forKey:的功能,并且还可以对对象内的类的属性进行赋值))

valueForKey:(根据key取值)

valueForKeyPath:(根据keyPath取值)

setValuesForKeysWithDictionary:(对模型进行一次性赋值)



2.KVC和对象的setter、getter方法的区别


一般情况下,KVC和setter、getter应该说都能达到对对象属性的赋值,并且KVC操作也是去调用的setter方法和getter方法(针对一些已经在.h中声明的属性而言)。但是对于一些私有属性,那么这个时候setter、getter方法就没有用了,这个时候KVC却能发挥重要优势。

例如:在Person.m中

#import "Person.h"

@implementation Person

{

??????????? NSInteger _height;

}

@end

此时你会发现setter、getter已经无能为力了,但是KVC去可以实现赋值、取值

[p setValue:@170 forKey:@"height"];




3.key和keyPath的区别


keyPath方法是集成了key的所有功能,也就是说对一个对象的一般属性进行赋值、取值,两个方法是通用的,都可以实现。但是对对象中的对象进的属性行赋值,只有keyPath能够实现。

setValuesForKeysWithDictionary:的巧妙使用(字典转模型)

-(instancetype)initWithDict:(NSDictionary *)dict{

if (self = [super init]) {

??????????? [self setValuesForKeysWithDictionary:dict];

}

???????? return self;

}




4.KVC进行求和,求平均值等操作


Person.h

#import?"Father.h"

#import?"Book.h"

@interface?Person?:?NSObject?{

@public

?????????? NSString?*_fullName;

@private

????????? NSString?*_name;

????????? Father?*_father;

????????? NSArray?*_books;

}

@end


Father.h

@interface?Father?:?NSObject?{

@protected

??????? NSString?*_name;

}

@end


Book.h

#import?

@interface?Book?:?NSObject?{

@private

??????????? NSString?*_name;

??????????? float?_price;

}

@end

使用代码:

#import?

#import?"Person.h"

int?main(int?argc,?const?char?*?argv[])

{

???????????? @autoreleasepool?{

???????????????????? Person?*person?=?[[Person?alloc]?init];

??????????????????? //直接访问public变量

?????????????????? person->_fullName?=?@"ALI?TOM";

?????????????????? NSLog(@"_fullName?:%@",person->_fullName);

?????????????? ?? //KVC方式

?????????????? ? ? [person?setValue:@"TOM"?forKey:@"_name"];

???????????????? ? NSLog(@"_name?:%@",?[person?valueForKey:@"_name"]);


?????????????????? Father *father = [[Father alloc] init];

?? ? ? ? ? ? ? ? ? [father?setValue:@"JACK"?forKey:@"_name"];

?????????????????? [person?setValue:father?forKey:@"_father"];

????????????????? //KVC路径访问

????????????????? NSLog(@"father.name?:%@",?[person?valueForKeyPath:@"_father._name"]);

????????????????? [person?setValue:@"JERRY"?forKeyPath:@"_father._name"];

?????????????????? NSLog(@"father.name?:%@",?[person?valueForKeyPath:@"_father._name"]);

????????????????? NSMutableArray *bookArray = [NSMutableArray arrayWithCapacity:3];

????????????? ? ? for?(int?i=0;?i<3;?i++)?{

???????????????????????? Book?*book?=?[[Book?alloc]?init];

???????????????????????? NSString?*bookName?=?[NSString?stringWithFormat:@"book%d",?i];

???????????????????????? [book?setValue:bookName?forKey:@"_name"];

???????????????????????? [book?setValue:@((i?+?1)??*?10.2)?forKey:@"_price"];

???????????????????????? [bookArray?addObject:book];

????????????????????????? [book?release];

? ? ? ? ? ? ? ? ? }

???????????????? [person setValue:bookArray forKey:@"_books"];

? ? ? ? ? ? ? ? ? ? //KVC计算

?????????????????? //通过@count获取集合book个数

?????????????????? NSNumber?*bookCount?=?[person?valueForKeyPath:@"_books.@count"];

?????????????????? NSLog(@"book?count?:%@",?bookCount);

?????????????????? //价格总和

?????????????????? NSNumber?*sum?=?[person?valueForKeyPath:@"_books.@sum._price"];

?????????????????? NSLog(@"sum?:%@",?sum);

?????????????????? //价格平均值

?????????????????? NSNumber?*avg?=?[person?valueForKeyPath:@"_books.@avg._price"];

?????????????????? NSLog(@"vag?:%@",?avg);

????????????????? //最低价格

???????????????? NSNumber?*min?=?[person?valueForKeyPath:@"_books.@min._price"];

???????????????? NSLog(@"min?:%@",?min);

???????????????? //最高价格

???????????????? NSNumber?*max?=?[person?valueForKeyPath:@"_books.@max._price"];

???????????????? NSLog(@"max?:%@",?max);






二.KVO


1.KVO的用法

KVO也就是key-value-observing(即键值观察),利用一个key来找到某个属性并监听其值得改变。用法如下:

添加观察者

在观察者中实现监听方法,observeValueForKeyPath: ofObject: change: context:(通过查阅文档可以知道,绝大多数对象都有这个方法,因为这个方法属于NSObject)

移除观察者

//让对象b监听对象a的name属性

//options属性可以选择是哪个

/* NSKeyValueObservingOptionNew =0x01, 新值

* NSKeyValueObservingOptionOld =0x02, 旧值

*/

[a addObserver:b forKeyPath:@"name"options:kNilOptionscontext:nil];

a.name = @"zzz";

#pragma mark - 实现KVO回调方法

/* * 当对象的属性发生改变会调用该方法

* @param keyPath 监听的属性

* @param object 监听的对象

* @param change 新值和旧值

* @param context 额外的数据

*/

- (void)observeValueForKeyPath:(NSString *)keyPathofObject:(id)objectchange:(NSDictionary*)change context:(void *)context{

????????????? NSLog(@"%@的值改变了,",keyPath);

? ? ? ? ? ?? NSLog(@"change:%@", change);

}

//最后不要忘记了,去移除observer

- (void)dealloc{

??????????? [a removeObserver:b forKeyPath:@"name"];

}

KVO底层(这部分涉及到了runtime,关于isa指针,会在随后的简述中介绍)

当一个类的属性被观察的时候,系统会通过runtime动态的创建一个该类的派生类,并且会在这个类中重写基类被观察的属性的setter方法,而且系统将这个类的isa指针指向了派生类,从而实现了给监听的属性赋值时调用的是派生类的setter方法。重写的setter方法会在调用原setter方法前后,通知观察对象值得改变。

具体实现图如下


1.当某个类的对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的 setter 方法。

2.派生类在被重写的 setter 方法中实现真正的通知机制,就如前面手动实现键值观察那样。这么做是基于设置属性会调用 setter 方法,而通过重写就获得了 KVO 需要的通知机制。当然前提是要通过遵循 KVO 的属性设置方式来变更属性值,如果仅是直接修改属性对应的成员变量,是无法实现 KVO 的。

3.同时派生类还重写了 class 方法以“欺骗”外部调用者它就是起初的那个类。然后系统将这个对象的 isa 指针指向这个新诞生的派生类,因此这个对象就成为该派生类的对象了,因而在该对象上对 setter 的调用就会调用重写的 setter,从而激活键值通知机制。此外,派生类还重写了 dealloc 方法来释放资源。





2.根据KVO底层原理自己实现KVO

#import "NSObject+HKKVO.h"

#import@implementation NSObject (QLKVO) //给NSObject增加分类

//自定义的KVO

-(void)QL_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context

{

?????????? //1.动态生成一个类!!

????????? //1.1获取类名

????????? NSString * oldClassName = NSStringFromClass([self class]);

????????? NSString * newClassName = [@"QLKVO_" stringByAppendingString:oldClassName];

????????? const char * name = [newClassName UTF8String];

???????? //动态创建一个子类

????????? Class MyClass = objc_allocateClassPair([self class], name, 0);

????????? //添加方法

???????? class_addMethod(MyClass, @selector(setName:), (IMP)setName, "v@:@");

????????? //注册类

??????? objc_registerClassPair(MyClass);

? ? ? ? //NSLog(@"%@", [self class]);? 会输出:Person

?????? ? //修改isa,修改完后self变成了子类

???????? object_setClass(self, MyClass);

? ? ?? //NSLog(@"%@", [self class]);? 会输出:hkKVO_Person

??????? //保存观察者对象,这里的self指的是子类

????? ? objc_setAssociatedObject(self, @"objc", observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}



void setName(id self,SEL _cmd,NSString * newName){

???????? NSLog(@"我监听到了!!");

? ? ? ?? id class = [self class];

??????? //拿到观察者

??????? id objc = objc_getAssociatedObject(self, @"objc");

??????? //改变self的isa指针,指向父类

?????? object_setClass(self, class_getSuperclass(class));

?????? //调用父类的set方法!!

?????? objc_msgSend(self, @selector(setName:),newName);

???? ?? //? ? NSLog(@"修改完毕!!");

?

?????? //通知观察者

????? ? objc_msgSend(objc, @selector(observeValueForKeyPath:ofObject:change:context:),self,@"name",nil,nil);

??????? //改回子类类型(如果不改,self就指向了父类,下次父类的name属性更改的,就不会调用到这个函数里面去)

???????? object_setClass(self, class);

}



#import "NSObject+HKKVO.h"

@interface ViewController ()

/**? */@property(nonatomic,strong)Person * p;

@end@implementation ViewController

- (void)viewDidLoad {??

???????? [super viewDidLoad];? ?

?????? ? Person * p = [[Person alloc]init];

? ? ? ? ?? _p = p;??

?????????? //使用自定义的KVO来监听!Person 的 name 属性?

? ? ? ? ? [p hk_addObserver:self forKeyPath:@"name" options:0 context:nil];??

?????????? NSLog(@"%@",[p class]);??

}

//监听到了就来了!!

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

????????? NSLog(@"哥么来了!!!%@",_p.name);

}

//点击就改变!!

- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent *)event {

??????? static int i = 0;

??????? i++;

?????? _p.name = [NSString stringWithFormat:@"%d",i];

}

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

推荐阅读更多精彩内容

  • 概念 先来看看概念,Key-value coding (KVC) 和 key-value observing (K...
    wuwy阅读 1,301评论 0 1
  • 在iOS开发中,我们常常用到键值编码KVC和键值监听KVO两个东东,今天小编和大家分享的就是这两个东东在应用开发中...
    突然自我阅读 990评论 2 3
  • 在编程中,最常见的就是程序的流程取决于你所使用的各种变量和属性的值,根据变量和属性的值确定后面运行的代码,有时会检...
    pro648阅读 1,639评论 2 27
  • KVC和KVO都属于键值编程而且底层实现机制都是isa-swizzing 一.KVC概述 1.kvc 是一种通过(...
    俊俊吖阅读 144评论 0 0
  • 遍历遇到左括号一律进栈,右括号与栈顶的数比较,注意判断栈不能为空,另外返回时也要检查栈是否空
    Genejing阅读 189评论 0 0