使用Block实现KVO.doc_第1页
使用Block实现KVO.doc_第2页
使用Block实现KVO.doc_第3页
使用Block实现KVO.doc_第4页
使用Block实现KVO.doc_第5页
已阅读5页,还剩10页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

使用Block实现KVOSJKVOController的用法只需要引入NSObject+SJKVOController.h头文件就可以使用SJKVOController。 先看一下它的头文件:#import #import SJKVOHeader.hinterface NSObject (SJKVOController)/= add observer =/- (void)sj_addObserver:(NSObject *)observer forKeys:(NSArray *)keys withBlock:(SJKVOBlock)block;- (void)sj_addObserver:(NSObject *)observer forKey:(NSString *)key withBlock:(SJKVOBlock)block;/= remove observer =/- (void)sj_removeObserver:(NSObject *)observer forKeys:(NSArray *)keys;- (void)sj_removeObserver:(NSObject *)observer forKey:(NSString *)key;- (void)sj_removeObserver:(NSObject *)observer;- (void)sj_removeAllObservers;/= list observers =/- (void)sj_listAllObservers;end从上面的API可以看出,这个小轮子: 1. 支持一次观察同一对象的多个属性。 2. 可以一次只观察一个对象的一个属性。 3. 可以移除对某个对象对多个属性的观察。 4. 可以移除对某个对象对某个属性的观察。 5. 可以移除某个观察自己的对象。 6. 可以移除所有观察自己的对象。 6. 打印出所有观察自己的对象的信息,包括对象本身,观察的属性,setter方法。下面来结合Demo讲解一下如何使用这个小轮子:在点击上面两个按钮中的任意一个,增加观察:一次性添加:- (IBAction)addObserversTogether:(UIButton *)sender NSArray *keys = number,color; self.model sj_addObserver:self forKeys:keys withBlock:(id observedObject, NSString *key, id oldValue, id newValue) if (key isEqualToString:number) dispatch_async(dispatch_get_main_queue(), self.numberLabel.text = NSString stringWithFormat:%,newValue; ); else if (key isEqualToString:color) dispatch_async(dispatch_get_main_queue(), self.numberLabel.backgroundColor = newValue; ); ;分两次添加:- (IBAction)addObserverSeparatedly:(UIButton *)sender self.model sj_addObserver:self forKey:number withBlock:(id observedObject, NSString *key, id oldValue, id newValue) dispatch_async(dispatch_get_main_queue(), self.numberLabel.text = NSString stringWithFormat:%,newValue; ); ; self.model sj_addObserver:self forKey:color withBlock:(id observedObject, NSString *key, id oldValue, id newValue) dispatch_async(dispatch_get_main_queue(), self.numberLabel.backgroundColor = newValue; ); ;添加以后,点击最下面的按钮来显示所有的观察信息:- (IBAction)showAllObservingItems:(UIButton *)sender self.model sj_listAllObservers;输出:SJKVOController80499:4242749 SJKVOLog:= Start Listing All Observers: = SJKVOController80499:4242749 SJKVOLog:observer item:observer: | key: color | setter: setColor:SJKVOController80499:4242749 SJKVOLog:observer item:observer: | key: number | setter: setNumber:在这里我重写了description方法,打印出了每个观察的对象和key,以及setter方法。现在点击更新按钮,则会更新model的number和color属性,从而触发KVO:- (IBAction)updateNumber:(UIButton *)sender /trigger KVO : number NSInteger newNumber = arc4random() % 100; self.model.number = NSNumber numberWithInteger:newNumber; /trigger KVO : color NSArray *colors = UIColor redColor,UIColor yellowColor,UIColor blueColor,UIColor greenColor; NSInteger colorIndex = arc4random() % 3; self.model.color = colorscolorIndex;我们可以看到中间的Label上面显示的数字和背景色都在变化,成功实现了KVO:现在我们移除观察,点击remove按钮- (IBAction)removeAllObservingItems:(UIButton *)sender self.model sj_removeAllObservers; 在移除了所有的观察者以后,则会打印出:SJKVOController80499:4242749 SJKVOLog:Removed all obserbing objects of object:而且如果在这个时候打印观察者list,则会输出:SJKVOController80499:4242749 SJKVOLog:There is no observers obserbing object:需要注意的是,这里的移除可以有多种选择:可以移某个对象的某个key,也可以移除某个对象的几个keys,为了验证,我们可以结合list方法来验证一下移除是否成功:验证1:在添加number和color的观察后,移除nunber的观察:- (IBAction)removeAllObservingItems:(UIButton *)sender self.model sj_removeObserver:self forKey:number;在移除以后,我们调用list方法,输出:SJKVOController80850:4278383 SJKVOLog:= Start Listing All Observers: =SJKVOController80850:4278383 SJKVOLog:observer item:observer: | key: color | setter: setColor:现在只有color属性被观察了。看一下实际的效果:我们可以看到,现在只有color在变,而数字没有变化了,验证此移除方法正确。验证2:在添加number和color的观察后,移除nunber和color的观察:- (IBAction)removeAllObservingItems:(UIButton *)sender self.model sj_removeObserver:self forKeys:number,color;在移除以后,我们调用list方法,输出:SJKVOController80901:4283311 SJKVOLog:There is no observers obserbing object:现在color和number属性都不被观察了。看一下实际的效果:我们可以看到,现在color和number都不变了,验证此移除方法正确。OK,现在知道了怎么用SJKVOController,我下面给大家看一下代码:SJKVOController代码解析先大致讲解一下SJKVOController的实现思路: 1. 为了减少侵入性,SJKVOController被设计为NSObject的一个分类。 2. SJKVOController仿照了KVO的实现思路,在添加观察以后在运行时动态生成当前类的子类,给这个子类添加被观察的属性的set方法并使用isa swizzle的方式将当前对象转换为当前类的子类的实现。 3. 同时,这个子类还使用了关联对象来保存一个“观察项”的set,每一个观察项封装了一次观察的行为(有去重机制):包括观察自己的对象,自己被观察的属性,以及传进来的block。 4. 在当前类,也就是子类的set方法被调用的时候做三件事情: - 第一件事情是使用KVC来找出当前属性的旧值。 - 第二件事情是调用父类(原来的类)的set方法(设新值)。 - 第三件事是根据当前的观察对象和key,在观察项set里面找出对应的block并调用。再来看一下这个小轮子的几个类:SJKVOController:实现KVO主要功能的类。SJKVOObserverItem:封装观察项的类。SJKVOTool:setter和getter的相互转换和相关运行时查询方法等。SJKVOError:封装错误类型。SJKVOHeader:引用了运行时的头文件。下面开始一个一个来讲解每个类的源码:SJKVOController再看一下头文件:#import #import SJKVOHeader.hinterface NSObject (SJKVOController)/= add observer =/- (void)sj_addObserver:(NSObject *)observer forKeys:(NSArray *)keys withBlock:(SJKVOBlock)block;- (void)sj_addObserver:(NSObject *)observer forKey:(NSString *)key withBlock:(SJKVOBlock)block;/= remove observer =/- (void)sj_removeObserver:(NSObject *)observer forKeys:(NSArray *)keys;- (void)sj_removeObserver:(NSObject *)observer forKey:(NSString *)key;- (void)sj_removeObserver:(NSObject *)observer;- (void)sj_removeAllObservers;/= list observers =/- (void)sj_listAllObservers;end每个方法的意思相信读者已经能看懂了,现在讲一下具体的实现。从sj_addObserver:forKey withBlock:开始:sj_addObserver:forKey withBlock:方法:除去一些错误的判断,该方法作了下面几件事情:1.判断当前被观察的类是否存在与传入key对应的setter方法:SEL setterSelector = NSSelectorFromString(SJKVOTool setterFromGetter:key);Method setterMethod = SJKVOTool objc_methodFromClass:self class selector:setterSelector;/error: no corresponding setter mothodif (!setterMethod) SJLog(%,SJKVOError errorNoMatchingSetterForKey:key); return;2. 如果有,判断当前被观察到类是否已经是KVO类(在KVO机制中,如果某个对象一旦被观察,则这个对象就变成了带有包含KVO前缀的类的实例)。如果已经是KVO类,则将当前实例的isa指针指向其父类(最开始被观察的类): /get original class(current class,may be KVO class) NSString *originalClassName = NSStringFromClass(OriginalClass); /如果当前的类是带有KVO前缀的类(也就是已经被观察到类),则需要将KVO前缀的类删除,并讲 if (originalClassName hasPrefix:SJKVOClassPrefix) /now,the OriginalClass is KVO class, we should destroy it and make new one Class CurrentKVOClass = OriginalClass; object_setClass(self, class_getSuperclass(OriginalClass); objc_disposeClassPair(CurrentKVOClass); originalClassName=originalClassName substringFromIndex:(SJKVOClassPrefix.length); 3. 如果不是KVO类(说明当前实例没有被观察),则创建一个带有KVO前缀的类,并将当前实例的isa指针指向这个新建的类: /create a KVO class Class KVOClass = self createKVOClassFromOriginalClassName:originalClassName; /swizzle isa from self to KVO class object_setClass(self, KVOClass);看一下如何新建一个新的类:- (Class)createKVOClassFromOriginalClassName:(NSString *)originalClassName NSString *kvoClassName = SJKVOClassPrefix stringByAppendingString:originalClassName; Class KVOClass = NSClassFromString(kvoClassName); / KVO class already exists if (KVOClass) return KVOClass; / if there is no KVO class, then create one KVOClass = objc_allocateClassPair(OriginalClass, kvoClassName.UTF8String, 0);/OriginalClass is super class / pretending to be the original class:return the super class in class method Method clazzMethod = class_getInstanceMethod(OriginalClass, selector(class); class_addMethod(KVOClass, selector(class), (IMP)return_original_class, method_getTypeEncoding(clazzMethod); / finally, register this new KVO class objc_registerClassPair(KVOClass); return KVOClass;4. 查看观察项set,如果这个set里面有已经保存的观察项,则需要新建一个空的观察项set,将已经保存的观察项放入这个新建的set里面: /if we already have some history observer items, we should add them into new KVO class NSMutableSet* observers = objc_getAssociatedObject(self, &SJKVOObservers); if (observers.count 0) NSMutableSet *newObservers = NSMutableSet alloc initWithCapacity:5; objc_setAssociatedObject(self, &SJKVOObservers, newObservers, OBJC_ASSOCIATION_RETAIN_NONATOMIC); for (SJKVOObserverItem *item in observers) self KVOConfigurationWithObserver:item.observer key:item.key block:item.block kvoClass:KVOClass setterSelector:item.setterSelector setterMethod:setterMethod; 看一下如何保存观察项的:- (void)KVOConfigurationWithObserver:(NSObject *)observer key:(NSString *)key block:(SJKVOBlock)block kvoClass:(Class)kvoClass setterSelector:(SEL)setterSelector setterMethod:(Method)setterMethod /add setter method in KVO Class if(!SJKVOTool detectClass:OriginalClass hasSelector:setterSelector) class_addMethod(kvoClass, setterSelector, (IMP)kvo_setter_implementation, method_getTypeEncoding(setterMethod); /add item of this observer&key pair self addObserverItem:observer key:key setterSelector:setterSelector setterMethod:setterMethod block:block;这里首先给KVO类增加了setter方法:/implementation of KVO setter methodvoid kvo_setter_implementation(id self, SEL _cmd, id newValue) NSString *setterName = NSStringFromSelector(_cmd); NSString *getterName = SJKVOTool getterFromSetter:setterName; if (!getterName) SJLog(%,SJKVOError errorTransferSetterToGetterFaildedWithSetterName:setterName); return; / create a super class of a specific instance Class superclass = class_getSuperclass(OriginalClass); struct objc_super superclass_to_call = .super_class = superclass, /super class .receiver = self, /insatance of this class ; / cast method pointer void (*objc_msgSendSuperCasted)(void *, SEL, id) = (void *)objc_msgSendSuper; / call supers setter, the supper is the original class objc_msgSendSuperCasted(&superclass_to_call, _cmd, newValue); / look up observers and call the blocks NSMutableSet *observers = objc_getAssociatedObject(self,&SJKVOObservers); if (observers.count 0) for (SJKVOObserverItem *item in observers) if ( (item.observer = observer) & item.key isEqualToString:key) findSameObserverAndKey = YES; if (!findSameObserverAndKey) self KVOConfigurationWithObserver:observer key:key block:block kvoClass:KVOClass setterSelector:setterSelector setterMethod:setterMethod; 而一次性添加多个key的方法,也只是调用多次一次性添加单个key的方法罢了:- (void)sj_addObserver:(NSObject *)observer forKeys:(NSArray *)keys withBlock:(SJKVOBlock)block /error: keys array is nil or no elements if (keys.count = 0) SJLog(%,SJKVOError errorInvalidInputObservingKeys); return; /one key corresponding to one specific item, not the observer keys enumerateObjectsUsingBlock:(NSString * key, NSUInteger idx, BOOL * _Nonnull stop) self sj_addObserver:observer forKey:key withBlock:block; ;关于移除观察的实现,只是在观察项set里面找出封装了对应的观察对象和key的观察项就可以了:- (void)sj_removeObserver:(NSObject *)observer forKey:(NSString *)key NSMutableSet* observers = objc_getAssociatedObject(self, &SJKVOObservers); if (observers.count 0) SJKVOObserverItem *removingItem = nil; for (SJKVOObserverItem* item in observers) if (item.observer = observer & item.key isEqualToString:key) removingItem = item; break; if (removingItem) observers removeObject:removingItem; 再看一下移除所有观察者:- (void)sj_removeAllObservers NSMutableSet* observers = objc_getAssociatedObject(self, &SJKVOObservers); if (observers.count 0) observers removeAllObjects; SJLog(SJKVOLog:Removed all obserbing objects of object:%,self); else SJLog(SJKVOLog:There is no observers obserbing object:%,self); SJKVOObserverItem这个类负责封装每一个观察项的信息,包括: - 观察者对象。 - 被观察的key。 - setter方法名(SEL) - setter方法(Method) - 回调的block需要注意的是: 在这个小轮子里,对于同一个对象可以观察不同的key的情况,是将这两个key区分开来的,是属于不同的观察项。所以应该用不同的SJKVOObserverItem实例来封装。#import #import typedef void(SJKVOBlock)(id observedObject, NSString *key, id oldValue, id newValue);interface SJKVOObserverItem : NSObjectproperty (nonatomic, strong) NSObject *observer;property (nonatomic, copy) NSString *key;property (nonatomic, assign) SEL setterSelector;property (nonatomic, assign) Method setterMethod;property (nonatomic, copy) SJKVOBlock block;- (instance

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论