Whether it's an MVC or an MVVM architecture, we all need to be able to synchronize the model changes to the relevant parts in time. Just like the monthly observer model, you can do this in iOS with KVO, but it's always a way to get the code out of order. Here you can use Thbinder on GitHub to accomplish this task. At the same time I do a bit of this code, so I use a simple macro to complete, do not save the Thbinder instance.
#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,swizzledeallocselec TOR)) 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);}};i MP newdeallocimp = Imp_implementationwithblock (Newdealloc); if (!class_addmethod (Classtoswizzle, DeallocSelector, Newdeallocimp, "[email protected]:") {//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 was called at the time of Setting.originaldealloc = (__ty peof__ (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
You also need to add a method to the Thbinder so that you can say that the binder is stored in an instance that needs to be observed.
-(ID) Initforbindingfromobject: (ID) fromobject keypath: (NSString *) Fromkeypath Toobject: (ID) toobject K Eypath: (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:transform Ationblock (Change[nskeyvaluechangenewkey]) Forkeypath:mytokeypath]; } copy]; } else {changeblock = [^ (nsdictionary *change) {[Wtoobject setvalue:change[nskeyvaluechangenew Key] Forkeypath:mytokeypath]; } copy]; } _observer = [Thobserver observerforobject:fromobject Keypath:fro Mkeypath Options:nskeyvalueobservingoptionnew Changeblock : Changeblock]; <span style= "color: #ff0000;" > [Fromobject setbinder:self keypath:fromkeypath];</span>} return self;}
Here, I used the Reactivecocoa inside the macro, to organize the Tmbind macro. It is OK to use this macro as long as it is used. In the Thbinder, the produced binder is automatically placed into the observed instance.
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