不管是用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