使用Block实现KVO.docx

上传人:b****8 文档编号:30000033 上传时间:2023-08-04 格式:DOCX 页数:19 大小:66.14KB
下载 相关 举报
使用Block实现KVO.docx_第1页
第1页 / 共19页
使用Block实现KVO.docx_第2页
第2页 / 共19页
使用Block实现KVO.docx_第3页
第3页 / 共19页
使用Block实现KVO.docx_第4页
第4页 / 共19页
使用Block实现KVO.docx_第5页
第5页 / 共19页
点击查看更多>>
下载资源
资源描述

使用Block实现KVO.docx

《使用Block实现KVO.docx》由会员分享,可在线阅读,更多相关《使用Block实现KVO.docx(19页珍藏版)》请在冰豆网上搜索。

使用Block实现KVO.docx

使用Block实现KVO

使用Block实现KVO

SJKVOController的用法

只需要引入NSObject+SJKVOController.h头文件就可以使用SJKVOController。

先看一下它的头文件:

#import

#import"SJKVOHeader.h"

@interfaceNSObject(SJKVOController)

//==============addobserver===============//

-(void)sj_addObserver:

(NSObject*)observerforKeys:

(NSArray*)keyswithBlock:

(SJKVOBlock)block;

-(void)sj_addObserver:

(NSObject*)observerforKey:

(NSString*)keywithBlock:

(SJKVOBlock)block;

//=============removeobserver=============//

-(void)sj_removeObserver:

(NSObject*)observerforKeys:

(NSArray*)keys;

-(void)sj_removeObserver:

(NSObject*)observerforKey:

(NSString*)key;

-(void)sj_removeObserver:

(NSObject*)observer;

-(void)sj_removeAllObservers;

//=============listobservers===============//

-(void)sj_listAllObservers;

@end

从上面的API可以看出,这个小轮子:

1.支持一次观察同一对象的多个属性。

2.可以一次只观察一个对象的一个属性。

3.可以移除对某个对象对多个属性的观察。

4.可以移除对某个对象对某个属性的观察。

5.可以移除某个观察自己的对象。

6.可以移除所有观察自己的对象。

6.打印出所有观察自己的对象的信息,包括对象本身,观察的属性,setter方法。

下面来结合Demo讲解一下如何使用这个小轮子:

在点击上面两个按钮中的任意一个,增加观察:

一次性添加:

-(IBAction)addObserversTogether:

(UIButton*)sender{

NSArray*keys=@[@"number",@"color"];

[self.modelsj_addObserver:

selfforKeys:

keyswithBlock:

^(idobservedObject,NSString*key,idoldValue,idnewValue){

if([keyisEqualToString:

@"number"]){

dispatch_async(dispatch_get_main_queue(),^{

self.numberLabel.text=[NSStringstringWithFormat:

@"%@",newValue];

});

}elseif([keyisEqualToString:

@"color"]){

dispatch_async(dispatch_get_main_queue(),^{

self.numberLabel.backgroundColor=newValue;

});

}

}];

}

分两次添加:

-(IBAction)addObserverSeparatedly:

(UIButton*)sender{

[self.modelsj_addObserver:

selfforKey:

@"number"withBlock:

^(idobservedObject,NSString*key,idoldValue,idnewValue){

dispatch_async(dispatch_get_main_queue(),^{

self.numberLabel.text=[NSStringstringWithFormat:

@"%@",newValue];

});

}];

[self.modelsj_addObserver:

selfforKey:

@"color"withBlock:

^(idobservedObject,NSString*key,idoldValue,idnewValue){

dispatch_async(dispatch_get_main_queue(),^{

self.numberLabel.backgroundColor=newValue;

});

}];

}

添加以后,点击最下面的按钮来显示所有的观察信息:

-(IBAction)showAllObservingItems:

(UIButton*)sender{

[self.modelsj_listAllObservers];

}

输出:

SJKVOController[80499:

4242749]SJKVOLog:

====================StartListingAllObservers:

====================

SJKVOController[80499:

4242749]SJKVOLog:

observeritem:

{observer:

0x7fa1577054f0>|key:

color|setter:

setColor:

}

SJKVOController[80499:

4242749]SJKVOLog:

observeritem:

{observer:

0x7fa1577054f0>|key:

number|setter:

setNumber:

}

在这里我重写了description方法,打印出了每个观察的对象和key,以及setter方法。

现在点击更新按钮,则会更新model的number和color属性,从而触发KVO:

-(IBAction)updateNumber:

(UIButton*)sender{

//triggerKVO:

number

NSIntegernewNumber=arc4random()%100;

self.model.number=[NSNumbernumberWithInteger:

newNumber];

//triggerKVO:

color

NSArray*colors=@[[UIColorredColor],[UIColoryellowColor],[UIColorblueColor],[UIColorgreenColor]];

NSIntegercolorIndex=arc4random()%3;

self.model.color=colors[colorIndex];

}

我们可以看到中间的Label上面显示的数字和背景色都在变化,成功实现了KVO:

现在我们移除观察,点击remove按钮

-(IBAction)removeAllObservingItems:

(UIButton*)sender{

[self.modelsj_removeAllObservers];

}

在移除了所有的观察者以后,则会打印出:

SJKVOController[80499:

4242749]SJKVOLog:

Removedallobserbingobjectsofobject:

0x60000003b700>

而且如果在这个时候打印观察者list,则会输出:

SJKVOController[80499:

4242749]SJKVOLog:

Thereisnoobserversobserbingobject:

0x60000003b700>

需要注意的是,这里的移除可以有多种选择:

可以移某个对象的某个key,也可以移除某个对象的几个keys,为了验证,我们可以结合list方法来验证一下移除是否成功:

验证1:

在添加number和color的观察后,移除nunber的观察:

-(IBAction)removeAllObservingItems:

(UIButton*)sender{

[self.modelsj_removeObserver:

selfforKey:

@"number"];

}

在移除以后,我们调用list方法,输出:

SJKVOController[80850:

4278383]SJKVOLog:

====================StartListingAllObservers:

====================

SJKVOController[80850:

4278383]SJKVOLog:

observeritem:

{observer:

0x7ffeec408560>|key:

color|setter:

setColor:

}

现在只有color属性被观察了。

看一下实际的效果:

我们可以看到,现在只有color在变,而数字没有变化了,验证此移除方法正确。

验证2:

在添加number和color的观察后,移除nunber和color的观察:

-(IBAction)removeAllObservingItems:

(UIButton*)sender{

[self.modelsj_removeObserver:

selfforKeys:

@[@"number",@"color"]];

}

在移除以后,我们调用list方法,输出:

SJKVOController[80901:

4283311]SJKVOLog:

Thereisnoobserversobserbingobject:

0x600000220fa0>

现在color和number属性都不被观察了。

看一下实际的效果:

我们可以看到,现在color和number都不变了,验证此移除方法正确。

OK,现在知道了怎么用SJKVOController,我下面给大家看一下代码:

SJKVOController代码解析

先大致讲解一下SJKVOController的实现思路:

1.为了减少侵入性,SJKVOController被设计为NSObject的一个分类。

2.SJKVOController仿照了KVO的实现思路,在添加观察以后在运行时动态生成当前类的子类,给这个子类添加被观察的属性的set方法并使用isaswizzle的方式将当前对象转换为当前类的子类的实现。

3.同时,这个子类还使用了关联对象来保存一个“观察项”的set,每一个观察项封装了一次观察的行为(有去重机制):

包括观察自己的对象,自己被观察的属性,以及传进来的block。

4.在当前类,也就是子类的set方法被调用的时候做三件事情:

-第一件事情是使用KVC来找出当前属性的旧值。

-第二件事情是调用父类(原来的类)的set方法(设新值)。

-第三件事是根据当前的观察对象和key,在观察项set里面找出对应的block并调用。

再来看一下这个小轮子的几个类:

SJKVOController:

实现KVO主要功能的类。

SJKVOObserverItem:

封装观察项的类。

SJKVOTool:

setter和getter的相互转换和相关运行时查询方法等。

SJKVOError:

封装错误类型。

SJKVOHeader:

引用了运行时的头文件。

下面开始一个一个来讲解每个类的源码:

SJKVOController

再看一下头文件:

#import

#import"SJKVOHeader.h"

@interfaceNSObject(SJKVOController)

//==============addobserver===============//

-(void)sj_addObserver:

(NSObject*)observerforKeys:

(NSArray*)keyswithBlock:

(SJKVOBlock)block;

-(void)sj_addObserver:

(NSObject*)observerforKey:

(NSString*)keywithBlock:

(SJKVOBlock)block;

//=============removeobserver=============//

-(void)sj_removeObserver:

(NSObject*)observerforKeys:

(NSArray*)keys;

-(void)sj_removeObserver:

(NSObject*)observerforKey:

(NSString*)key;

-(void)sj_removeObserver:

(NSObject*)observer;

-(void)sj_removeAllObservers;

//=============listobservers===============//

-(void)sj_listAllObservers;

@end

每个方法的意思相信读者已经能看懂了,现在讲一下具体的实现。

从sj_addObserver:

forKeywithBlock:

开始:

sj_addObserver:

forKeywithBlock:

方法:

除去一些错误的判断,该方法作了下面几件事情:

1.判断当前被观察的类是否存在与传入key对应的setter方法:

SELsetterSelector=NSSelectorFromString([SJKVOToolsetterFromGetter:

key]);

MethodsetterMethod=[SJKVOToolobjc_methodFromClass:

[selfclass]selector:

setterSelector];

//error:

nocorrespondingsettermothod

if(!

setterMethod){

SJLog(@"%@",[SJKVOErrorerrorNoMatchingSetterForKey:

key]);

return;

}

2.如果有,判断当前被观察到类是否已经是KVO类(在KVO机制中,如果某个对象一旦被观察,则这个对象就变成了带有包含KVO前缀的类的实例)。

如果已经是KVO类,则将当前实例的isa指针指向其父类(最开始被观察的类):

//getoriginalclass(currentclass,maybeKVOclass)

NSString*originalClassName=NSStringFromClass(OriginalClass);

//如果当前的类是带有KVO前缀的类(也就是已经被观察到类),则需要将KVO前缀的类删除,并讲

if([originalClassNamehasPrefix:

SJKVOClassPrefix]){

//now,theOriginalClassisKVOclass,weshoulddestroyitandmakenewone

ClassCurrentKVOClass=OriginalClass;

object_setClass(self,class_getSuperclass(OriginalClass));

objc_disposeClassPair(CurrentKVOClass);

originalClassName=[originalClassNamesubstringFromIndex:

(SJKVOClassPrefix.length)];

}

3.如果不是KVO类(说明当前实例没有被观察),则创建一个带有KVO前缀的类,并将当前实例的isa指针指向这个新建的类:

//createaKVOclass

ClassKVOClass=[selfcreateKVOClassFromOriginalClassName:

originalClassName];

//swizzleisafromselftoKVOclass

object_setClass(self,KVOClass);

看一下如何新建一个新的类:

-(Class)createKVOClassFromOriginalClassName:

(NSString*)originalClassName

{

NSString*kvoClassName=[SJKVOClassPrefixstringByAppendingString:

originalClassName];

ClassKVOClass=NSClassFromString(kvoClassName);

//KVOclassalreadyexists

if(KVOClass){

returnKVOClass;

}

//ifthereisnoKVOclass,thencreateone

KVOClass=objc_allocateClassPair(OriginalClass,kvoClassName.UTF8String,0);//OriginalClassissuperclass

//pretendingtobetheoriginalclass:

returnthesuperclassinclassmethod

MethodclazzMethod=class_getInstanceMethod(OriginalClass,@selector(class));

class_addMethod(KVOClass,@selector(class),(IMP)return_original_class,method_getTypeEncoding(clazzMethod));

//finally,registerthisnewKVOclass

objc_registerClassPair(KVOClass);

returnKVOClass;

}

4.查看观察项set,如果这个set里面有已经保存的观察项,则需要新建一个空的观察项set,将已经保存的观察项放入这个新建的set里面:

//ifwealreadyhavesomehistoryobserveritems,weshouldaddthemintonewKVOclass

NSMutableSet*observers=objc_getAssociatedObject(self,&SJKVOObservers);

if(observers.count>0){

NSMutableSet*newObservers=[[NSMutableSetalloc]initWithCapacity:

5];

objc_setAssociatedObject(self,&SJKVOObservers,newObservers,OBJC_ASSOCIATION_RETAIN_NONATOMIC);

for(SJKVOObserverItem*iteminobservers){

[selfKVOConfigurationWithObserver:

item.observerkey:

item.keyblock:

item.blockkvoClass:

KVOClasssetterSelector:

item.setterSelectorsetterMethod:

setterMethod];

}

}

看一下如何保存观察项的:

-(void)KVOConfigurationWithObserver:

(NSObject*)observerkey:

(NSString*)keyblock:

(SJKVOBlock)blockkvoClass:

(Class)kvoClasssetterSelector:

(SEL)setterSelectorsetterMethod:

(Method)setterMethod

{

//addsettermethodinKVOClass

if(!

[SJKVOTooldetectClass:

OriginalClasshasSelector:

setterSelector]){

class_addMethod(kvoClass,setterSelector,(IMP)kvo_setter_implementation,method_getTypeEncoding(setterMethod));

}

//additemofthisobserver&&keypair

[selfaddObserverItem:

observerkey:

keysetterSelector:

setterSelectorsetterMethod:

setterMethodblock:

block];

}

这里首先给KVO类增加了setter方法:

//implementationofKVOsettermethod

voidkvo_setter_implementation(idself,SEL_cmd,idnewValue)

{

NSString*setterName=NSStringFromSelector(_cmd);

NSString*getterName=[SJKVOToolgetterFromSetter:

setterName];

if(!

getterName){

SJLog(@"%@",[SJKVOErrorerrorTransferSetterToGetterFaildedWithSetterName:

setterName]);

return;

}

//createasuperclassofaspecificinstance

Classsuperclass=class_getSuperclass(OriginalClass);

structobjc_supersuperclass_to_call={

.super_class=superclass,//superclass

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > PPT模板 > 其它模板

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1