使用Block实现KVO.docx
- 文档编号:2151278
- 上传时间:2023-05-02
- 格式:DOCX
- 页数:19
- 大小:66.14KB
使用Block实现KVO.docx
《使用Block实现KVO.docx》由会员分享,可在线阅读,更多相关《使用Block实现KVO.docx(19页珍藏版)》请在冰点文库上搜索。
使用Block实现KVO
使用Block实现KVO
SJKVOController的用法
只需要引入NSObject+SJKVOController.h头文件就可以使用SJKVOController。
先看一下它的头文件:
#import
#import"SJKVOHeader.h"
@interfaceNSObject(SJKVOController)
//==============addobserver===============//
-(void)sj_addObserver:
(NSObject*)observerforKeys:
(NSArray
(SJKVOBlock)block;
-(void)sj_addObserver:
(NSObject*)observerforKey:
(NSString*)keywithBlock:
(SJKVOBlock)block;
//=============removeobserver=============//
-(void)sj_removeObserver:
(NSObject*)observerforKeys:
(NSArray
-(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 (SJKVOBlock)block; -(void)sj_addObserver: (NSObject*)observerforKey: (NSString*)keywithBlock: (SJKVOBlock)block; //=============removeobserver=============// -(void)sj_removeObserver: (NSObject*)observerforKeys: (NSArray -(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文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 使用 Block 实现 KVO