ios Bind綁定 – iPhone手機開發技術文章 iPhone軟體開發教學課程

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

同時需要在THBinder裡面加入一個方法,這樣就可以講binder保存到需要被觀察的實例裡面。

- (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;
}

在這裡,我用瞭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

You May Also Like