不管是用mvc還是mvvm的架構,我們都需要一點就是model的改變能夠及時同步到相關部件中。就類似月觀察者模型,在ios中可以通過kvo來完成這樣的事情,但是每次都是用這個樣的方式,就回讓代碼混亂。在這裡可以采用THBinder在github來完成這個任務。同時我對這個代碼進行了一點處理,這樣就使用一個簡單的宏來完成,不要保存THBinder實例了。
#import "THBinder.h" #import "THObserver.h" #import#import #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]; [fromObject setBinder:self keyPath:fromKeyPath]; } return self; }
// // 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