不管是用mvc还是mvvm的架构,我们都需要一点就是model的改变能够及时同步到相关部件中。就类似月观察者模型,在ios中可以通过kvo来完成这样的事情,但是每次都是用这个样的方式,就回让代码混乱。在这里可以采用THBinder在github来完成这个任务。同时我对这个代码进行了一点处理,这样就使用一个简单的宏来完成,不要保存THBinder实例了。
#import "THBinder.h" #import "THObserver.h" #import <objc/runtime.h> #import <objc/message.h> #define TMBindDictoryKey "__TMBindDictoryKey" #define BindKey(target,keyPath) [NSString stringWithFormat:@"__binder__%@",keyPath] static NSMutableSet *swizzledClasses() { static dispatch_once_t onceToken; static NSMutableSet *swizzledClasses = nil; dispatch_once(&onceToken, ^{ swizzledClasses = [[NSMutableSet alloc] init]; }); return swizzledClasses; } static void swizzleDeallocIfNeeded(Class classToSwizzle) { @synchronized (swizzledClasses()) { NSString *className = NSStringFromClass(classToSwizzle); if ([swizzledClasses() containsObject:className]) return; SEL deallocSelector = sel_registerName("dealloc"); SEL swizzleDeallocSelector = sel_registerName("swizzleDelloc"); __block void (*originalDealloc)(__unsafe_unretained id, SEL) = NULL; id newDealloc = ^(__unsafe_unretained id self) { if(class_respondsToSelector(classToSwizzle,swizzleDeallocSelector)) objc_msgSend(self,swizzleDeallocSelector); if (originalDealloc == NULL) { struct objc_super superInfo = { .receiver = self, .super_class = class_getSuperclass(classToSwizzle) }; objc_msgSendSuper(&superInfo, deallocSelector); } else { originalDealloc(self, deallocSelector); } }; IMP newDeallocIMP = imp_implementationWithBlock(newDealloc); if (!class_addMethod(classToSwizzle, deallocSelector, newDeallocIMP, "v@:")) { // The class already contains a method implementation. Method deallocMethod = class_getInstanceMethod(classToSwizzle, deallocSelector); // We need to store original implementation before setting new implementation // in case method is called at the time of setting. originalDealloc = (__typeof__(originalDealloc))method_getImplementation(deallocMethod); // We need to store original implementation again, in case it just changed. originalDealloc = (__typeof__(originalDealloc))method_setImplementation(deallocMethod, newDeallocIMP); } [swizzledClasses() addObject:className]; } } @interface NSObject (SupportBinding) - (void)setBinder:(id)binder keyPath:(NSString*)keyPath; @end @implementation NSObject (SupportBinding) - (void)swizzleDelloc{ NSMutableDictionary* bindDict = objc_getAssociatedObject(self,TMBindDictoryKey); [bindDict enumerateKeysAndObjectsUsingBlock:^(id key, NSArray *obj, BOOL *stop) { [obj enumerateObjectsUsingBlock:^(THBinder* binder, NSUInteger idx, BOOL *stop) { [binder stopBinding]; }]; }]; objc_setAssociatedObject(self, TMBindDictoryKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (void)setBinder:(id)binder keyPath:(NSString*)keyPath{ NSMutableDictionary* bindDict = objc_getAssociatedObject(self,TMBindDictoryKey); if(!bindDict){ bindDict = [NSMutableDictionary new]; objc_setAssociatedObject(self, TMBindDictoryKey, bindDict, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } NSString* keyName = BindKey(self, keyPath); id object = [bindDict valueForKey:keyName]; if([object containsObject:binder]){ return; } if(!object){ object = [NSMutableArray new]; } [object addObject:binder]; [bindDict setValue:object forKey:keyName]; swizzleDeallocIfNeeded(self.class); } @end
- (id)initForBindingFromObject:(id)fromObject keyPath:(NSString *)fromKeyPath toObject:(id)toObject keyPath:(NSString *)toKeyPath transformationBlock:(THBinderTransformationBlock)transformationBlock { if((self = [super init])) { __weak id wToObject = toObject; NSString *myToKeyPath = [toKeyPath copy]; THObserverBlockWithChangeDictionary changeBlock; if(transformationBlock) { changeBlock = [^(NSDictionary *change) { [wToObject setValue:transformationBlock(change[NSKeyValueChangeNewKey]) forKeyPath:myToKeyPath]; } copy]; } else { changeBlock = [^(NSDictionary *change) { [wToObject setValue:change[NSKeyValueChangeNewKey] forKeyPath:myToKeyPath]; } copy]; } _observer = [THObserver observerForObject:fromObject keyPath:fromKeyPath options:NSKeyValueObservingOptionNew changeBlock:changeBlock]; <span style="color:#ff0000;"> [fromObject setBinder:self keyPath:fromKeyPath];</span> } return self; }
在这里,我用了reactivecocoa里面的宏,来组织了这个TMBIND宏。这样使用的时候就只要这个宏就ok了。在THBinder里面会将生产的binder自动放到被观察的实例里面了。
// // Binder.h // KVODemo // // Created by Tommy on 14-6-13. // Copyright (c) 2014年 com.taobao. All rights reserved. // #ifndef KVODemo_Binder_h #define KVODemo_Binder_h #import "EXTKeyPathCoding.h" #import "THObserver.h" #import "THBinder.h" //one-way bind #define TMBIND(_fromObject_,_fromKeyPath_,_toObject_,_toKeyPath_) [THBinder binderFromObject:_fromObject_ keyPath:@keypath(_fromObject_, _fromKeyPath_) toObject:_toObject_ keyPath:@keypath(_toObject_,_toKeyPath_)] #define TMBIND_WITH_TRANSFORMBLOCK(_fromObject_,_fromKeyPath_,_toObject_,_toKeyPath_,_transfromBlock_) [THBinder binderFromObject:_fromObject_ keyPath:@keypath(_fromObject_, _fromKeyPath_) toObject:_toObject_ keyPath:@keypath(_toObject_,_toKeyPath_) transformationBlock:_transfromBlock_]; #endif