├── .gitignore ├── KVOBlockBinding ├── EXTKeyPathCoding.h ├── NSObject+KVOBlockBinding.h ├── NSObject+KVOBlockBinding.m ├── NSObject+WSKeyPathBinding.h ├── NSObject+WSKeyPathBinding.m ├── NSObject+WSObservation.h ├── NSObject+WSObservation.m ├── WSObservationBinding.h ├── WSObservationBinding.m └── metamacros.h ├── KVOBlockBindingExample.xcodeproj └── project.pbxproj ├── KVOBlockBindingExample ├── ExampleModel.h ├── ExampleModel.m ├── KVOBlockBinding-Info.plist ├── KVOBlockBinding-Prefix.pch ├── KVOBlockBindingAppDelegate.h ├── KVOBlockBindingAppDelegate.m ├── KVOBlockBindingViewController.h ├── KVOBlockBindingViewController.m ├── en.lproj │ ├── InfoPlist.strings │ ├── KVOBlockBindingViewController.xib │ └── MainWindow.xib └── main.m ├── KVOBlockBindingTests ├── KVOBlockBindingTests-Info.plist ├── KVOBlockBindingTests-Prefix.pch ├── KVOBlockBindingTests.h ├── KVOBlockBindingTests.m └── en.lproj │ └── InfoPlist.strings ├── LICENSE.md ├── README.md └── WSKVOBlockBinding.vendorspec /.gitignore: -------------------------------------------------------------------------------- 1 | project.xcworkspace 2 | xcuserdata 3 | 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /KVOBlockBinding/EXTKeyPathCoding.h: -------------------------------------------------------------------------------- 1 | // 2 | // EXTKeyPathCoding.h 3 | // extobjc 4 | // 5 | // Created by Justin Spahr-Summers on 19.06.12. 6 | // Copyright (C) 2012 Justin Spahr-Summers. 7 | // Released under the MIT license. 8 | // 9 | 10 | #import 11 | #import "metamacros.h" 12 | 13 | /** 14 | * \@keypath allows compile-time verification of key paths. Given a real object 15 | * receiver and key path: 16 | * 17 | * @code 18 | 19 | NSString *UTF8StringPath = @keypath(str.lowercaseString.UTF8String); 20 | // => @"lowercaseString.UTF8String" 21 | 22 | NSString *versionPath = @keypath(NSObject, version); 23 | // => @"version" 24 | 25 | NSString *lowercaseStringPath = @keypath(NSString.new, lowercaseString); 26 | // => @"lowercaseString" 27 | 28 | * @endcode 29 | * 30 | * ... the macro returns an \c NSString containing all but the first path 31 | * component or argument (e.g., @"lowercaseString.UTF8String", @"version"). 32 | * 33 | * In addition to simply creating a key path, this macro ensures that the key 34 | * path is valid at compile-time (causing a syntax error if not), and supports 35 | * refactoring, such that changing the name of the property will also update any 36 | * uses of \@keypath. 37 | */ 38 | #define keypath(...) \ 39 | metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__))(keypath1(__VA_ARGS__))(keypath2(__VA_ARGS__)) 40 | 41 | #define keypath1(PATH) \ 42 | (((void)(NO && ((void)PATH, NO)), strchr(# PATH, '.') + 1)) 43 | 44 | #define keypath2(OBJ, PATH) \ 45 | (((void)(NO && ((void)OBJ.PATH, NO)), # PATH)) 46 | -------------------------------------------------------------------------------- /KVOBlockBinding/NSObject+KVOBlockBinding.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "WSObservationBinding.h" 3 | 4 | 5 | /** 6 | This interface is deprecated in favour of WSObservation 7 | **/ 8 | @interface NSObject (KVOBlockBinding) 9 | 10 | /** 11 | * Observe the keypath with the provided block while the returned object is retained 12 | * 13 | * @param keyPath The keypath of the property to observe using KVO 14 | * @param owner The owner of this binding, used to unbind all observers for a particular owner 15 | * @param options The ORd set of NSKeyValueObservingOptions 16 | * @param block the block that should be invoked when the keyPath changes 17 | * @return An object representing the binding, this does NOT need to be retained 18 | */ 19 | - (WSObservationBinding*)addObserverForKeyPath:(NSString*)keyPath 20 | owner:(id)owner 21 | options:(NSKeyValueObservingOptions)options 22 | block:(WSObservationBlock)block; 23 | 24 | /** 25 | * Same as above, except the default options are set to NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld 26 | * 27 | * @param keyPath The keypath of the property to observe using KVO 28 | * @param owner The owner of this binding, used to unbind all observers for a particular owner 29 | * @param block the block that should be invoked when the keyPath changes 30 | * @return An object representing the binding, this does NOT need to be retained 31 | */ 32 | - (WSObservationBinding*)addObserverForKeyPath:(NSString*)keyPath 33 | owner:(id)owner 34 | block:(WSObservationBlock)block; 35 | 36 | /** 37 | * Remove all block-based observers for the specified keypath on this object. 38 | * 39 | * @param keyPath The keypath of the property from which to remove observers 40 | */ 41 | - (void)removeAllBlockBasedObserversForKeyPath:(NSString*)keyPath; 42 | 43 | /** 44 | * Remove all block-based observers from this object. This does not affect traditional KVO observers. 45 | */ 46 | - (void)removeAllBlockBasedObservers; 47 | 48 | 49 | /** 50 | * Remove all block-based observers for the given owner, this should be called in dealloc to make sure that all bindings for an object are 51 | * discarded 52 | */ 53 | - (void)removeAllBlockBasedObserversForOwner:(id)owner; 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /KVOBlockBinding/NSObject+KVOBlockBinding.m: -------------------------------------------------------------------------------- 1 | #import "NSObject+KVOBlockBinding.h" 2 | #import 3 | 4 | #define ASSOCIATED_OBJ_OBSERVERS_KEY @"rayh_block_based_observers" 5 | 6 | @implementation NSObject (KVOBlockBinding) 7 | 8 | -(NSMutableArray*)allBlockBasedObservers 9 | { 10 | NSMutableArray *objects = objc_getAssociatedObject(self, ASSOCIATED_OBJ_OBSERVERS_KEY); 11 | if(!objects) { 12 | objects = [NSMutableArray array]; 13 | objc_setAssociatedObject(self, ASSOCIATED_OBJ_OBSERVERS_KEY, objects, OBJC_ASSOCIATION_RETAIN); 14 | } 15 | 16 | return objects; 17 | } 18 | 19 | - (void)removeAllBlockBasedObserversForKeyPath:(NSString*)keyPath 20 | { 21 | for(WSObservationBinding *binding in [NSArray arrayWithArray:[self allBlockBasedObservers]]) { 22 | if([binding.keyPath isEqualToString:keyPath]) { 23 | [binding invalidate]; 24 | [[self allBlockBasedObservers] removeObject:binding]; 25 | } 26 | } 27 | } 28 | 29 | - (void)removeAllBlockBasedObservers 30 | { 31 | for(WSObservationBinding *binding in [NSArray arrayWithArray:[self allBlockBasedObservers]]) { 32 | [binding invalidate]; 33 | [[self allBlockBasedObservers] removeObject:binding]; 34 | } 35 | } 36 | 37 | - (void)removeAllBlockBasedObserversForOwner:(id)owner 38 | { 39 | for(WSObservationBinding *binding in [NSArray arrayWithArray:[self allBlockBasedObservers]]) { 40 | if([binding.owner isEqual:owner]) { 41 | [binding invalidate]; 42 | [[self allBlockBasedObservers] removeObject:binding]; 43 | } 44 | } 45 | } 46 | 47 | -(WSObservationBinding*)addObserverForKeyPath:(NSString*)keyPath 48 | owner:(id)owner 49 | options:(NSKeyValueObservingOptions)options 50 | block:(WSObservationBlock)block 51 | { 52 | WSObservationBinding *binding = [[WSObservationBinding alloc] init]; 53 | binding.block = block; 54 | binding.observed = self; 55 | binding.keyPath = keyPath; 56 | binding.owner = owner; 57 | 58 | [[self allBlockBasedObservers] addObject:binding]; 59 | 60 | [self addObserver:binding forKeyPath:keyPath options:options context:nil]; 61 | 62 | return binding; 63 | } 64 | 65 | -(WSObservationBinding*)addObserverForKeyPath:(NSString*)keyPath 66 | owner:(id)owner 67 | block:(WSObservationBlock)block 68 | { 69 | return [self addObserverForKeyPath:keyPath 70 | owner:owner 71 | options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld 72 | block:block]; 73 | } 74 | 75 | @end -------------------------------------------------------------------------------- /KVOBlockBinding/NSObject+WSKeyPathBinding.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+WSKeyPathBinding.h 3 | // Local 4 | // 5 | // Created by Ray Hilton on 27/06/12. 6 | // Copyright (c) 2012 Wirestorm Pty Ltd. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSObject (WSKeyPathBinding) 12 | /** 13 | * Sets up keypath binding between the source and target objects. 14 | * 15 | * @param source The source object to observe 16 | * @param sourcePath The keypath of the property to observe on the source using KVO 17 | * @param target The target object to observe 18 | * @param targetPath The keypath of the property to observe on the target using KVO 19 | * @return An array containing the binding and it's reverse, if specified. This does NOT need to be retained 20 | */ 21 | - (void)bindSourceKeyPath:(NSString *)sourcePath 22 | to:(id)target 23 | targetKeyPath:(NSString *)targetPath 24 | reverseMapping:(BOOL)reverseMapping; 25 | 26 | - (void)unbindAllKeyPaths; 27 | 28 | - (void)unbindKeyPath:(NSString*)keyPath; 29 | @end 30 | -------------------------------------------------------------------------------- /KVOBlockBinding/NSObject+WSKeyPathBinding.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+WSKeyPathBinding.m 3 | // Local 4 | // 5 | // Created by Ray Hilton on 27/06/12. 6 | // Copyright (c) 2012 Wirestorm Pty Ltd. All rights reserved. 7 | // 8 | 9 | #import "NSObject+WSKeyPathBinding.h" 10 | #import "NSObject+WSObservation.h" 11 | #import 12 | 13 | 14 | #define ASSOCIATED_OBJ_BINDINGS_KEY @"rayh_block_based_bindings" 15 | 16 | @implementation NSObject (WSKeyPathBinding) 17 | 18 | -(NSMutableArray*)allKeyPathBindings 19 | { 20 | NSMutableArray *objects = objc_getAssociatedObject(self, ASSOCIATED_OBJ_BINDINGS_KEY); 21 | if(!objects) { 22 | objects = [NSMutableArray array]; 23 | objc_setAssociatedObject(self, ASSOCIATED_OBJ_BINDINGS_KEY, objects, OBJC_ASSOCIATION_RETAIN); 24 | } 25 | 26 | return objects; 27 | } 28 | 29 | 30 | - (void)bindSourceKeyPath:(NSString *)sourcePath to:(id)target targetKeyPath:(NSString *)targetPath reverseMapping:(BOOL)reverseMapping 31 | { 32 | [[self allKeyPathBindings] addObject:[self observe:self keyPath:sourcePath block:^(id observed, NSDictionary *change) { 33 | [target setValue:[change valueForKey:NSKeyValueChangeNewKey] forKey:targetPath]; 34 | }]]; 35 | 36 | if(reverseMapping) 37 | { 38 | [[self allKeyPathBindings] addObject:[self observe:target keyPath:targetPath block:^(id observed, NSDictionary *change) { 39 | [self setValue:[change valueForKey:NSKeyValueChangeNewKey] forKey:sourcePath]; 40 | }]]; 41 | } 42 | } 43 | 44 | - (void)unbindKeyPath:(NSString *)keyPath 45 | { 46 | NSArray *bindings = [self allKeyPathBindings]; 47 | for(WSObservationBinding *binding in bindings) 48 | { 49 | if([binding.keyPath isEqualToString:keyPath]) 50 | { 51 | [binding invalidate]; 52 | binding.block = nil; 53 | [[self allKeyPathBindings] removeObject:binding]; 54 | } 55 | } 56 | } 57 | 58 | - (void)unbindAllKeyPaths 59 | { 60 | for(WSObservationBinding *binding in [self allKeyPathBindings]) 61 | { 62 | [binding invalidate]; 63 | binding.block = nil; 64 | } 65 | 66 | [[self allKeyPathBindings] removeAllObjects]; 67 | } 68 | @end 69 | -------------------------------------------------------------------------------- /KVOBlockBinding/NSObject+WSObservation.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+WSObservation.h 3 | // Local 4 | // 5 | // Created by Ray Hilton on 27/06/12. 6 | // Copyright (c) 2012 Wirestorm Pty Ltd. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "WSObservationBinding.h" 11 | #import "EXTKeyPathCoding.h" 12 | 13 | @interface NSObject (WSObservation) 14 | 15 | /** 16 | * Remove all block-based observers for the specified object. 17 | * 18 | * @param object the object to stop observing 19 | */ 20 | - (void)removeAllObservationsOn:(id)object; 21 | 22 | /** 23 | * Remove all block-based observerations. This does not affect traditional KVO observers. 24 | */ 25 | - (void)removeAllObservations; 26 | 27 | 28 | /** 29 | * Remove all block-based observers for the specified object and keypath. 30 | * 31 | * @param object the object to stop observing 32 | * @param keyPath The keypath of the property from which to remove observers 33 | */ 34 | - (void)removeAllObserverationsOn:(id)object keyPath:(NSString*)keyPath; 35 | 36 | /** 37 | * Observe the keypath with the provided block while the returned object is retained 38 | * 39 | * @param object The object to observe 40 | * @param keyPath The keypath of the property to observe using KVO 41 | * @param options The ORd set of NSKeyValueObservingOptions 42 | * @param block the block that should be invoked when the keyPath changes 43 | * @return An object representing the binding, this does NOT need to be retained 44 | */ 45 | - (WSObservationBinding*)observe:(id)object 46 | keyPath:(NSString *)keyPath 47 | options:(NSKeyValueObservingOptions)options 48 | block:(WSObservationBlock)block; 49 | 50 | /** 51 | * Same as above, except the default options are set to NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld 52 | * 53 | * @param object The object to observe 54 | * @param keyPath The keypath of the property to observe using KVO 55 | * @param block the block that should be invoked when the keyPath changes 56 | * @return An object representing the binding, this does NOT need to be retained 57 | */ 58 | - (WSObservationBinding*)observe:(id)object 59 | keyPath:(NSString *)keyPath 60 | block:(WSObservationBlock)block; 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /KVOBlockBinding/NSObject+WSObservation.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+WSObservation.m 3 | // Local 4 | // 5 | // Created by Ray Hilton on 27/06/12. 6 | // Copyright (c) 2012 Wirestorm Pty Ltd. All rights reserved. 7 | // 8 | 9 | #import "NSObject+WSObservation.h" 10 | #import 11 | 12 | #define ASSOCIATED_OBJ_OBSERVING_KEY @"rayh_block_based_observing" 13 | 14 | @implementation NSObject (WSObservation) 15 | 16 | -(NSMutableArray*)allBlockBasedObservations 17 | { 18 | NSMutableArray *objects = objc_getAssociatedObject(self, ASSOCIATED_OBJ_OBSERVING_KEY); 19 | if(!objects) { 20 | objects = [NSMutableArray array]; 21 | objc_setAssociatedObject(self, ASSOCIATED_OBJ_OBSERVING_KEY, objects, OBJC_ASSOCIATION_RETAIN); 22 | } 23 | 24 | return objects; 25 | } 26 | 27 | - (void)removeAllObservationsOn:(id)object 28 | { 29 | for(WSObservationBinding *binding in [NSArray arrayWithArray:[self allBlockBasedObservations]]) { 30 | if([binding.observed isEqual:object]) { 31 | [binding invalidate]; 32 | [[self allBlockBasedObservations] removeObject:binding]; 33 | } 34 | } 35 | } 36 | 37 | - (void)removeAllObservations 38 | { 39 | for(WSObservationBinding *binding in [NSArray arrayWithArray:[self allBlockBasedObservations]]) { 40 | [binding invalidate]; 41 | [[self allBlockBasedObservations] removeObject:binding]; 42 | } 43 | } 44 | 45 | 46 | - (void)removeAllObserverationsOn:(id)object keyPath:(NSString*)keyPath 47 | { 48 | for(WSObservationBinding *binding in [NSArray arrayWithArray:[self allBlockBasedObservations]]) { 49 | if([binding.observed isEqual:object] && [binding.keyPath isEqualToString:keyPath]) { 50 | [binding invalidate]; 51 | [[self allBlockBasedObservations] removeObject:binding]; 52 | } 53 | } 54 | } 55 | 56 | -(WSObservationBinding*)observe:(id)object 57 | keyPath:(NSString *)keyPath 58 | options:(NSKeyValueObservingOptions)options 59 | block:(WSObservationBlock)block 60 | { 61 | WSObservationBinding *binding = [[WSObservationBinding alloc] init]; 62 | binding.block = block; 63 | binding.observed = object; 64 | binding.keyPath = keyPath; 65 | binding.owner = self; 66 | 67 | [[self allBlockBasedObservations] addObject:binding]; 68 | 69 | [object addObserver:binding forKeyPath:keyPath options:options context:nil]; 70 | 71 | return binding; 72 | } 73 | 74 | -(WSObservationBinding*)observe:(id)object 75 | keyPath:(NSString *)keyPath 76 | block:(WSObservationBlock)block 77 | { 78 | 79 | return [self observe:object 80 | keyPath:keyPath 81 | options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld 82 | block:block]; 83 | } 84 | 85 | @end -------------------------------------------------------------------------------- /KVOBlockBinding/WSObservationBinding.h: -------------------------------------------------------------------------------- 1 | // 2 | // WSObservationBinding.h 3 | // Local 4 | // 5 | // Created by Ray Hilton on 27/06/12. 6 | // Copyright (c) 2012 Wirestorm Pty Ltd. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef void (^WSObservationBlock)(id observed, NSDictionary *change); 12 | 13 | @interface WSObservationBinding : NSObject 14 | @property (nonatomic, assign) BOOL valid; 15 | @property (nonatomic, assign) id owner; 16 | @property (nonatomic, retain) NSString *keyPath; 17 | @property (nonatomic, copy) WSObservationBlock block; 18 | @property (nonatomic, assign) id observed; 19 | 20 | /** 21 | * If a reference to the binding is kept by the caller to addObserver... then it can use this method to selectively just 22 | * remove this binding, and leave all others for the same keypath intact 23 | */ 24 | - (void)invalidate; 25 | 26 | /** 27 | * Invoke the callback block directly. This can be used to force your UI to update after setting up the binding 28 | */ 29 | - (void)invoke; 30 | 31 | @end -------------------------------------------------------------------------------- /KVOBlockBinding/WSObservationBinding.m: -------------------------------------------------------------------------------- 1 | // 2 | // WSObservationBinding.m 3 | // Local 4 | // 5 | // Created by Ray Hilton on 27/06/12. 6 | // Copyright (c) 2012 Wirestorm Pty Ltd. All rights reserved. 7 | // 8 | 9 | #import "WSObservationBinding.h" 10 | 11 | @interface WSObservationBinding () 12 | @end 13 | 14 | @implementation WSObservationBinding 15 | @synthesize valid=valid_; 16 | @synthesize block=block_; 17 | @synthesize observed=observed_; 18 | @synthesize keyPath=keyPath_; 19 | @synthesize owner=owner_; 20 | 21 | - (id)init 22 | { 23 | if((self = [super init])) { 24 | self.valid = YES; 25 | } 26 | return self; 27 | 28 | } 29 | 30 | - (void)dealloc 31 | { 32 | if(self.valid) 33 | [self invalidate]; 34 | } 35 | 36 | - (void)observeValueForKeyPath:(NSString *)path 37 | ofObject:(id)object 38 | change:(NSDictionary *)change 39 | context:(void *)context 40 | { 41 | if(self.valid && ![[change valueForKey:NSKeyValueChangeNewKey] isEqual:[change valueForKey:NSKeyValueChangeOldKey]]) 42 | self.block(self.observed, change); 43 | } 44 | 45 | - (void)invalidate 46 | { 47 | [self.observed removeObserver:self forKeyPath:self.keyPath]; 48 | self.valid = NO; 49 | } 50 | 51 | - (void)invoke 52 | { 53 | self.block(self.observed, [NSDictionary dictionary]); 54 | } 55 | @end 56 | -------------------------------------------------------------------------------- /KVOBlockBinding/metamacros.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Macros for metaprogramming 3 | * ExtendedC 4 | * 5 | * Copyright (C) 2012 Justin Spahr-Summers 6 | * Released under the MIT license 7 | */ 8 | 9 | #ifndef EXTC_METAMACROS_H 10 | #define EXTC_METAMACROS_H 11 | 12 | #ifdef HAVE_CONFIG_H 13 | #include "config.h" 14 | #endif 15 | 16 | /** 17 | * Executes one or more expressions (which may have a void type, such as a call 18 | * to a function that returns no value) and always returns true. 19 | */ 20 | #define metamacro_exprify(...) \ 21 | ((__VA_ARGS__), true) 22 | 23 | /** 24 | * Returns a string representation of VALUE after full macro expansion. 25 | */ 26 | #define metamacro_stringify(VALUE) \ 27 | metamacro_stringify_(VALUE) 28 | 29 | /** 30 | * Returns A and B concatenated after full macro expansion. 31 | */ 32 | #define metamacro_concat(A, B) \ 33 | metamacro_concat_(A, B) 34 | 35 | /** 36 | * Returns the Nth variadic argument (starting from zero). At least 37 | * N + 1 variadic arguments must be given. N must be between zero and twenty, 38 | * inclusive. 39 | */ 40 | #define metamacro_at(N, ...) \ 41 | metamacro_concat(metamacro_at, N)(__VA_ARGS__) 42 | 43 | /** 44 | * Returns the number of arguments (up to twenty) provided to the macro. At 45 | * least one argument must be provided. 46 | * 47 | * Inspired by P99: http://p99.gforge.inria.fr 48 | */ 49 | #define metamacro_argcount(...) \ 50 | metamacro_at(20, __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1) 51 | 52 | /** 53 | * Identical to #metamacro_foreach_cxt, except that no CONTEXT argument is 54 | * given. Only the index and current argument will thus be passed to MACRO. 55 | */ 56 | #define metamacro_foreach(MACRO, SEP, ...) \ 57 | metamacro_foreach_cxt(metamacro_foreach_iter, SEP, MACRO, __VA_ARGS__) 58 | 59 | /** 60 | * For each consecutive variadic argument (up to twenty), MACRO is passed the 61 | * zero-based index of the current argument, CONTEXT, and then the argument 62 | * itself. The results of adjoining invocations of MACRO are then separated by 63 | * SEP. 64 | * 65 | * Inspired by P99: http://p99.gforge.inria.fr 66 | */ 67 | #define metamacro_foreach_cxt(MACRO, SEP, CONTEXT, ...) \ 68 | metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__) 69 | 70 | /** 71 | * Identical to #metamacro_foreach_cxt. This can be used when the former would 72 | * fail due to recursive macro expansion. 73 | */ 74 | #define metamacro_foreach_cxt_recursive(MACRO, SEP, CONTEXT, ...) \ 75 | metamacro_concat(metamacro_foreach_cxt_recursive, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__) 76 | 77 | /** 78 | * In consecutive order, appends each variadic argument (up to twenty) onto 79 | * BASE. The resulting concatenations are then separated by SEP. 80 | * 81 | * This is primarily useful to manipulate a list of macro invocations into instead 82 | * invoking a different, possibly related macro. 83 | */ 84 | #define metamacro_foreach_concat(BASE, SEP, ...) \ 85 | metamacro_foreach_cxt(metamacro_foreach_concat_iter, SEP, BASE, __VA_ARGS__) 86 | 87 | /** 88 | * Iterates COUNT times, each time invoking MACRO with the current index 89 | * (starting at zero) and CONTEXT. The results of adjoining invocations of MACRO 90 | * are then separated by SEP. 91 | * 92 | * COUNT must be an integer between zero and twenty, inclusive. 93 | */ 94 | #define metamacro_for_cxt(COUNT, MACRO, SEP, CONTEXT) \ 95 | metamacro_concat(metamacro_for_cxt, COUNT)(MACRO, SEP, CONTEXT) 96 | 97 | /** 98 | * Returns the first argument given. At least one argument must be provided. 99 | * 100 | * This is useful when implementing a variadic macro, where you may have only 101 | * one variadic argument, but no way to retrieve it (for example, because \c ... 102 | * always needs to match at least one argument). 103 | * 104 | * @code 105 | 106 | #define varmacro(...) \ 107 | metamacro_head(__VA_ARGS__) 108 | 109 | * @endcode 110 | */ 111 | #define metamacro_head(...) \ 112 | metamacro_head_(__VA_ARGS__, 0) 113 | 114 | /** 115 | * Returns every argument except the first. At least two arguments must be 116 | * provided. 117 | */ 118 | #define metamacro_tail(...) \ 119 | metamacro_tail_(__VA_ARGS__) 120 | 121 | /** 122 | * Returns the first N (up to twenty) variadic arguments as a new argument list. 123 | * At least N variadic arguments must be provided. 124 | */ 125 | #define metamacro_take(N, ...) \ 126 | metamacro_concat(metamacro_take, N)(__VA_ARGS__) 127 | 128 | /** 129 | * Removes the first N (up to twenty) variadic arguments from the given argument 130 | * list. At least N variadic arguments must be provided. 131 | */ 132 | #define metamacro_drop(N, ...) \ 133 | metamacro_concat(metamacro_drop, N)(__VA_ARGS__) 134 | 135 | /** 136 | * Decrements VAL, which must be a number between zero and twenty, inclusive. 137 | * 138 | * This is primarily useful when dealing with indexes and counts in 139 | * metaprogramming. 140 | */ 141 | #define metamacro_dec(VAL) \ 142 | metamacro_at(VAL, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19) 143 | 144 | /** 145 | * Increments VAL, which must be a number between zero and twenty, inclusive. 146 | * 147 | * This is primarily useful when dealing with indexes and counts in 148 | * metaprogramming. 149 | */ 150 | #define metamacro_inc(VAL) \ 151 | metamacro_at(VAL, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21) 152 | 153 | /** 154 | * If A is equal to B, the next argument list is expanded; otherwise, the 155 | * argument list after that is expanded. A and B must be numbers between zero 156 | * and twenty, inclusive. Additionally, B must be greater than or equal to A. 157 | * 158 | * @code 159 | 160 | // expands to true 161 | metamacro_if_eq(0, 0)(true)(false) 162 | 163 | // expands to false 164 | metamacro_if_eq(0, 1)(true)(false) 165 | 166 | * @endcode 167 | * 168 | * This is primarily useful when dealing with indexes and counts in 169 | * metaprogramming. 170 | */ 171 | #define metamacro_if_eq(A, B) \ 172 | metamacro_concat(metamacro_if_eq, A)(B) 173 | 174 | /** 175 | * Identical to #metamacro_if_eq. This can be used when the former would fail 176 | * due to recursive macro expansion. 177 | */ 178 | #define metamacro_if_eq_recursive(A, B) \ 179 | metamacro_concat(metamacro_if_eq_recursive, A)(B) 180 | 181 | /** 182 | * Returns 1 if N is an even number, or 0 otherwise. N must be between zero and 183 | * twenty, inclusive. 184 | * 185 | * For the purposes of this test, zero is considered even. 186 | */ 187 | #define metamacro_is_even(N) \ 188 | metamacro_at(N, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1) 189 | 190 | /** 191 | * Returns the logical NOT of B, which must be the number zero or one. 192 | */ 193 | #define metamacro_not(B) \ 194 | metamacro_at(B, 1, 0) 195 | 196 | // IMPLEMENTATION DETAILS FOLLOW! 197 | // Do not write code that depends on anything below this line. 198 | #define metamacro_stringify_(VALUE) # VALUE 199 | #define metamacro_concat_(A, B) A ## B 200 | #define metamacro_foreach_iter(INDEX, MACRO, ARG) MACRO(INDEX, ARG) 201 | #define metamacro_head_(FIRST, ...) FIRST 202 | #define metamacro_tail_(FIRST, ...) __VA_ARGS__ 203 | #define metamacro_consume_(...) 204 | #define metamacro_expand_(...) __VA_ARGS__ 205 | 206 | // implemented from scratch so that metamacro_concat() doesn't end up nesting 207 | #define metamacro_foreach_concat_iter(INDEX, BASE, ARG) metamacro_foreach_concat_iter_(BASE, ARG) 208 | #define metamacro_foreach_concat_iter_(BASE, ARG) BASE ## ARG 209 | 210 | // metamacro_at expansions 211 | #define metamacro_at0(...) metamacro_head(__VA_ARGS__) 212 | #define metamacro_at1(_0, ...) metamacro_head(__VA_ARGS__) 213 | #define metamacro_at2(_0, _1, ...) metamacro_head(__VA_ARGS__) 214 | #define metamacro_at3(_0, _1, _2, ...) metamacro_head(__VA_ARGS__) 215 | #define metamacro_at4(_0, _1, _2, _3, ...) metamacro_head(__VA_ARGS__) 216 | #define metamacro_at5(_0, _1, _2, _3, _4, ...) metamacro_head(__VA_ARGS__) 217 | #define metamacro_at6(_0, _1, _2, _3, _4, _5, ...) metamacro_head(__VA_ARGS__) 218 | #define metamacro_at7(_0, _1, _2, _3, _4, _5, _6, ...) metamacro_head(__VA_ARGS__) 219 | #define metamacro_at8(_0, _1, _2, _3, _4, _5, _6, _7, ...) metamacro_head(__VA_ARGS__) 220 | #define metamacro_at9(_0, _1, _2, _3, _4, _5, _6, _7, _8, ...) metamacro_head(__VA_ARGS__) 221 | #define metamacro_at10(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, ...) metamacro_head(__VA_ARGS__) 222 | #define metamacro_at11(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, ...) metamacro_head(__VA_ARGS__) 223 | #define metamacro_at12(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, ...) metamacro_head(__VA_ARGS__) 224 | #define metamacro_at13(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, ...) metamacro_head(__VA_ARGS__) 225 | #define metamacro_at14(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, ...) metamacro_head(__VA_ARGS__) 226 | #define metamacro_at15(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, ...) metamacro_head(__VA_ARGS__) 227 | #define metamacro_at16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) metamacro_head(__VA_ARGS__) 228 | #define metamacro_at17(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, ...) metamacro_head(__VA_ARGS__) 229 | #define metamacro_at18(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, ...) metamacro_head(__VA_ARGS__) 230 | #define metamacro_at19(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, ...) metamacro_head(__VA_ARGS__) 231 | #define metamacro_at20(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, ...) metamacro_head(__VA_ARGS__) 232 | 233 | // metamacro_foreach_cxt expansions 234 | #define metamacro_foreach_cxt0(MACRO, SEP, CONTEXT) 235 | #define metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0) 236 | 237 | #define metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \ 238 | metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) \ 239 | SEP \ 240 | MACRO(1, CONTEXT, _1) 241 | 242 | #define metamacro_foreach_cxt3(MACRO, SEP, CONTEXT, _0, _1, _2) \ 243 | metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \ 244 | SEP \ 245 | MACRO(2, CONTEXT, _2) 246 | 247 | #define metamacro_foreach_cxt4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \ 248 | metamacro_foreach_cxt3(MACRO, SEP, CONTEXT, _0, _1, _2) \ 249 | SEP \ 250 | MACRO(3, CONTEXT, _3) 251 | 252 | #define metamacro_foreach_cxt5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \ 253 | metamacro_foreach_cxt4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \ 254 | SEP \ 255 | MACRO(4, CONTEXT, _4) 256 | 257 | #define metamacro_foreach_cxt6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \ 258 | metamacro_foreach_cxt5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \ 259 | SEP \ 260 | MACRO(5, CONTEXT, _5) 261 | 262 | #define metamacro_foreach_cxt7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \ 263 | metamacro_foreach_cxt6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \ 264 | SEP \ 265 | MACRO(6, CONTEXT, _6) 266 | 267 | #define metamacro_foreach_cxt8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \ 268 | metamacro_foreach_cxt7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \ 269 | SEP \ 270 | MACRO(7, CONTEXT, _7) 271 | 272 | #define metamacro_foreach_cxt9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \ 273 | metamacro_foreach_cxt8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \ 274 | SEP \ 275 | MACRO(8, CONTEXT, _8) 276 | 277 | #define metamacro_foreach_cxt10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ 278 | metamacro_foreach_cxt9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \ 279 | SEP \ 280 | MACRO(9, CONTEXT, _9) 281 | 282 | #define metamacro_foreach_cxt11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ 283 | metamacro_foreach_cxt10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ 284 | SEP \ 285 | MACRO(10, CONTEXT, _10) 286 | 287 | #define metamacro_foreach_cxt12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ 288 | metamacro_foreach_cxt11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ 289 | SEP \ 290 | MACRO(11, CONTEXT, _11) 291 | 292 | #define metamacro_foreach_cxt13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \ 293 | metamacro_foreach_cxt12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ 294 | SEP \ 295 | MACRO(12, CONTEXT, _12) 296 | 297 | #define metamacro_foreach_cxt14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \ 298 | metamacro_foreach_cxt13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \ 299 | SEP \ 300 | MACRO(13, CONTEXT, _13) 301 | 302 | #define metamacro_foreach_cxt15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \ 303 | metamacro_foreach_cxt14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \ 304 | SEP \ 305 | MACRO(14, CONTEXT, _14) 306 | 307 | #define metamacro_foreach_cxt16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \ 308 | metamacro_foreach_cxt15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \ 309 | SEP \ 310 | MACRO(15, CONTEXT, _15) 311 | 312 | #define metamacro_foreach_cxt17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \ 313 | metamacro_foreach_cxt16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \ 314 | SEP \ 315 | MACRO(16, CONTEXT, _16) 316 | 317 | #define metamacro_foreach_cxt18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \ 318 | metamacro_foreach_cxt17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \ 319 | SEP \ 320 | MACRO(17, CONTEXT, _17) 321 | 322 | #define metamacro_foreach_cxt19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \ 323 | metamacro_foreach_cxt18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \ 324 | SEP \ 325 | MACRO(18, CONTEXT, _18) 326 | 327 | #define metamacro_foreach_cxt20(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19) \ 328 | metamacro_foreach_cxt19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \ 329 | SEP \ 330 | MACRO(19, CONTEXT, _19) 331 | 332 | // metamacro_foreach_cxt_recursive expansions 333 | #define metamacro_foreach_cxt_recursive0(MACRO, SEP, CONTEXT) 334 | #define metamacro_foreach_cxt_recursive1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0) 335 | 336 | #define metamacro_foreach_cxt_recursive2(MACRO, SEP, CONTEXT, _0, _1) \ 337 | metamacro_foreach_cxt_recursive1(MACRO, SEP, CONTEXT, _0) \ 338 | SEP \ 339 | MACRO(1, CONTEXT, _1) 340 | 341 | #define metamacro_foreach_cxt_recursive3(MACRO, SEP, CONTEXT, _0, _1, _2) \ 342 | metamacro_foreach_cxt_recursive2(MACRO, SEP, CONTEXT, _0, _1) \ 343 | SEP \ 344 | MACRO(2, CONTEXT, _2) 345 | 346 | #define metamacro_foreach_cxt_recursive4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \ 347 | metamacro_foreach_cxt_recursive3(MACRO, SEP, CONTEXT, _0, _1, _2) \ 348 | SEP \ 349 | MACRO(3, CONTEXT, _3) 350 | 351 | #define metamacro_foreach_cxt_recursive5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \ 352 | metamacro_foreach_cxt_recursive4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \ 353 | SEP \ 354 | MACRO(4, CONTEXT, _4) 355 | 356 | #define metamacro_foreach_cxt_recursive6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \ 357 | metamacro_foreach_cxt_recursive5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \ 358 | SEP \ 359 | MACRO(5, CONTEXT, _5) 360 | 361 | #define metamacro_foreach_cxt_recursive7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \ 362 | metamacro_foreach_cxt_recursive6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \ 363 | SEP \ 364 | MACRO(6, CONTEXT, _6) 365 | 366 | #define metamacro_foreach_cxt_recursive8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \ 367 | metamacro_foreach_cxt_recursive7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \ 368 | SEP \ 369 | MACRO(7, CONTEXT, _7) 370 | 371 | #define metamacro_foreach_cxt_recursive9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \ 372 | metamacro_foreach_cxt_recursive8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \ 373 | SEP \ 374 | MACRO(8, CONTEXT, _8) 375 | 376 | #define metamacro_foreach_cxt_recursive10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ 377 | metamacro_foreach_cxt_recursive9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \ 378 | SEP \ 379 | MACRO(9, CONTEXT, _9) 380 | 381 | #define metamacro_foreach_cxt_recursive11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ 382 | metamacro_foreach_cxt_recursive10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ 383 | SEP \ 384 | MACRO(10, CONTEXT, _10) 385 | 386 | #define metamacro_foreach_cxt_recursive12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ 387 | metamacro_foreach_cxt_recursive11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ 388 | SEP \ 389 | MACRO(11, CONTEXT, _11) 390 | 391 | #define metamacro_foreach_cxt_recursive13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \ 392 | metamacro_foreach_cxt_recursive12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ 393 | SEP \ 394 | MACRO(12, CONTEXT, _12) 395 | 396 | #define metamacro_foreach_cxt_recursive14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \ 397 | metamacro_foreach_cxt_recursive13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \ 398 | SEP \ 399 | MACRO(13, CONTEXT, _13) 400 | 401 | #define metamacro_foreach_cxt_recursive15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \ 402 | metamacro_foreach_cxt_recursive14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \ 403 | SEP \ 404 | MACRO(14, CONTEXT, _14) 405 | 406 | #define metamacro_foreach_cxt_recursive16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \ 407 | metamacro_foreach_cxt_recursive15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \ 408 | SEP \ 409 | MACRO(15, CONTEXT, _15) 410 | 411 | #define metamacro_foreach_cxt_recursive17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \ 412 | metamacro_foreach_cxt_recursive16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \ 413 | SEP \ 414 | MACRO(16, CONTEXT, _16) 415 | 416 | #define metamacro_foreach_cxt_recursive18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \ 417 | metamacro_foreach_cxt_recursive17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \ 418 | SEP \ 419 | MACRO(17, CONTEXT, _17) 420 | 421 | #define metamacro_foreach_cxt_recursive19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \ 422 | metamacro_foreach_cxt_recursive18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \ 423 | SEP \ 424 | MACRO(18, CONTEXT, _18) 425 | 426 | #define metamacro_foreach_cxt_recursive20(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19) \ 427 | metamacro_foreach_cxt_recursive19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \ 428 | SEP \ 429 | MACRO(19, CONTEXT, _19) 430 | 431 | // metamacro_for_cxt expansions 432 | #define metamacro_for_cxt0(MACRO, SEP, CONTEXT) 433 | #define metamacro_for_cxt1(MACRO, SEP, CONTEXT) MACRO(0, CONTEXT) 434 | 435 | #define metamacro_for_cxt2(MACRO, SEP, CONTEXT) \ 436 | metamacro_for_cxt1(MACRO, SEP, CONTEXT) \ 437 | SEP \ 438 | MACRO(1, CONTEXT) 439 | 440 | #define metamacro_for_cxt3(MACRO, SEP, CONTEXT) \ 441 | metamacro_for_cxt2(MACRO, SEP, CONTEXT) \ 442 | SEP \ 443 | MACRO(2, CONTEXT) 444 | 445 | #define metamacro_for_cxt4(MACRO, SEP, CONTEXT) \ 446 | metamacro_for_cxt3(MACRO, SEP, CONTEXT) \ 447 | SEP \ 448 | MACRO(3, CONTEXT) 449 | 450 | #define metamacro_for_cxt5(MACRO, SEP, CONTEXT) \ 451 | metamacro_for_cxt4(MACRO, SEP, CONTEXT) \ 452 | SEP \ 453 | MACRO(4, CONTEXT) 454 | 455 | #define metamacro_for_cxt6(MACRO, SEP, CONTEXT) \ 456 | metamacro_for_cxt5(MACRO, SEP, CONTEXT) \ 457 | SEP \ 458 | MACRO(5, CONTEXT) 459 | 460 | #define metamacro_for_cxt7(MACRO, SEP, CONTEXT) \ 461 | metamacro_for_cxt6(MACRO, SEP, CONTEXT) \ 462 | SEP \ 463 | MACRO(6, CONTEXT) 464 | 465 | #define metamacro_for_cxt8(MACRO, SEP, CONTEXT) \ 466 | metamacro_for_cxt7(MACRO, SEP, CONTEXT) \ 467 | SEP \ 468 | MACRO(7, CONTEXT) 469 | 470 | #define metamacro_for_cxt9(MACRO, SEP, CONTEXT) \ 471 | metamacro_for_cxt8(MACRO, SEP, CONTEXT) \ 472 | SEP \ 473 | MACRO(8, CONTEXT) 474 | 475 | #define metamacro_for_cxt10(MACRO, SEP, CONTEXT) \ 476 | metamacro_for_cxt9(MACRO, SEP, CONTEXT) \ 477 | SEP \ 478 | MACRO(9, CONTEXT) 479 | 480 | #define metamacro_for_cxt11(MACRO, SEP, CONTEXT) \ 481 | metamacro_for_cxt10(MACRO, SEP, CONTEXT) \ 482 | SEP \ 483 | MACRO(10, CONTEXT) 484 | 485 | #define metamacro_for_cxt12(MACRO, SEP, CONTEXT) \ 486 | metamacro_for_cxt11(MACRO, SEP, CONTEXT) \ 487 | SEP \ 488 | MACRO(11, CONTEXT) 489 | 490 | #define metamacro_for_cxt13(MACRO, SEP, CONTEXT) \ 491 | metamacro_for_cxt12(MACRO, SEP, CONTEXT) \ 492 | SEP \ 493 | MACRO(12, CONTEXT) 494 | 495 | #define metamacro_for_cxt14(MACRO, SEP, CONTEXT) \ 496 | metamacro_for_cxt13(MACRO, SEP, CONTEXT) \ 497 | SEP \ 498 | MACRO(13, CONTEXT) 499 | 500 | #define metamacro_for_cxt15(MACRO, SEP, CONTEXT) \ 501 | metamacro_for_cxt14(MACRO, SEP, CONTEXT) \ 502 | SEP \ 503 | MACRO(14, CONTEXT) 504 | 505 | #define metamacro_for_cxt16(MACRO, SEP, CONTEXT) \ 506 | metamacro_for_cxt15(MACRO, SEP, CONTEXT) \ 507 | SEP \ 508 | MACRO(15, CONTEXT) 509 | 510 | #define metamacro_for_cxt17(MACRO, SEP, CONTEXT) \ 511 | metamacro_for_cxt16(MACRO, SEP, CONTEXT) \ 512 | SEP \ 513 | MACRO(16, CONTEXT) 514 | 515 | #define metamacro_for_cxt18(MACRO, SEP, CONTEXT) \ 516 | metamacro_for_cxt17(MACRO, SEP, CONTEXT) \ 517 | SEP \ 518 | MACRO(17, CONTEXT) 519 | 520 | #define metamacro_for_cxt19(MACRO, SEP, CONTEXT) \ 521 | metamacro_for_cxt18(MACRO, SEP, CONTEXT) \ 522 | SEP \ 523 | MACRO(18, CONTEXT) 524 | 525 | #define metamacro_for_cxt20(MACRO, SEP, CONTEXT) \ 526 | metamacro_for_cxt19(MACRO, SEP, CONTEXT) \ 527 | SEP \ 528 | MACRO(19, CONTEXT) 529 | 530 | // metamacro_if_eq expansions 531 | #define metamacro_if_eq0(VALUE) \ 532 | metamacro_concat(metamacro_if_eq0_, VALUE) 533 | 534 | #define metamacro_if_eq0_0(...) __VA_ARGS__ metamacro_consume_ 535 | #define metamacro_if_eq0_1(...) metamacro_expand_ 536 | #define metamacro_if_eq0_2(...) metamacro_expand_ 537 | #define metamacro_if_eq0_3(...) metamacro_expand_ 538 | #define metamacro_if_eq0_4(...) metamacro_expand_ 539 | #define metamacro_if_eq0_5(...) metamacro_expand_ 540 | #define metamacro_if_eq0_6(...) metamacro_expand_ 541 | #define metamacro_if_eq0_7(...) metamacro_expand_ 542 | #define metamacro_if_eq0_8(...) metamacro_expand_ 543 | #define metamacro_if_eq0_9(...) metamacro_expand_ 544 | #define metamacro_if_eq0_10(...) metamacro_expand_ 545 | #define metamacro_if_eq0_11(...) metamacro_expand_ 546 | #define metamacro_if_eq0_12(...) metamacro_expand_ 547 | #define metamacro_if_eq0_13(...) metamacro_expand_ 548 | #define metamacro_if_eq0_14(...) metamacro_expand_ 549 | #define metamacro_if_eq0_15(...) metamacro_expand_ 550 | #define metamacro_if_eq0_16(...) metamacro_expand_ 551 | #define metamacro_if_eq0_17(...) metamacro_expand_ 552 | #define metamacro_if_eq0_18(...) metamacro_expand_ 553 | #define metamacro_if_eq0_19(...) metamacro_expand_ 554 | #define metamacro_if_eq0_20(...) metamacro_expand_ 555 | 556 | #define metamacro_if_eq1(VALUE) metamacro_if_eq0(metamacro_dec(VALUE)) 557 | #define metamacro_if_eq2(VALUE) metamacro_if_eq1(metamacro_dec(VALUE)) 558 | #define metamacro_if_eq3(VALUE) metamacro_if_eq2(metamacro_dec(VALUE)) 559 | #define metamacro_if_eq4(VALUE) metamacro_if_eq3(metamacro_dec(VALUE)) 560 | #define metamacro_if_eq5(VALUE) metamacro_if_eq4(metamacro_dec(VALUE)) 561 | #define metamacro_if_eq6(VALUE) metamacro_if_eq5(metamacro_dec(VALUE)) 562 | #define metamacro_if_eq7(VALUE) metamacro_if_eq6(metamacro_dec(VALUE)) 563 | #define metamacro_if_eq8(VALUE) metamacro_if_eq7(metamacro_dec(VALUE)) 564 | #define metamacro_if_eq9(VALUE) metamacro_if_eq8(metamacro_dec(VALUE)) 565 | #define metamacro_if_eq10(VALUE) metamacro_if_eq9(metamacro_dec(VALUE)) 566 | #define metamacro_if_eq11(VALUE) metamacro_if_eq10(metamacro_dec(VALUE)) 567 | #define metamacro_if_eq12(VALUE) metamacro_if_eq11(metamacro_dec(VALUE)) 568 | #define metamacro_if_eq13(VALUE) metamacro_if_eq12(metamacro_dec(VALUE)) 569 | #define metamacro_if_eq14(VALUE) metamacro_if_eq13(metamacro_dec(VALUE)) 570 | #define metamacro_if_eq15(VALUE) metamacro_if_eq14(metamacro_dec(VALUE)) 571 | #define metamacro_if_eq16(VALUE) metamacro_if_eq15(metamacro_dec(VALUE)) 572 | #define metamacro_if_eq17(VALUE) metamacro_if_eq16(metamacro_dec(VALUE)) 573 | #define metamacro_if_eq18(VALUE) metamacro_if_eq17(metamacro_dec(VALUE)) 574 | #define metamacro_if_eq19(VALUE) metamacro_if_eq18(metamacro_dec(VALUE)) 575 | #define metamacro_if_eq20(VALUE) metamacro_if_eq19(metamacro_dec(VALUE)) 576 | 577 | // metamacro_if_eq_recursive expansions 578 | #define metamacro_if_eq_recursive0(VALUE) \ 579 | metamacro_concat(metamacro_if_eq_recursive0_, VALUE) 580 | 581 | #define metamacro_if_eq_recursive0_0(...) __VA_ARGS__ metamacro_consume_ 582 | #define metamacro_if_eq_recursive0_1(...) metamacro_expand_ 583 | #define metamacro_if_eq_recursive0_2(...) metamacro_expand_ 584 | #define metamacro_if_eq_recursive0_3(...) metamacro_expand_ 585 | #define metamacro_if_eq_recursive0_4(...) metamacro_expand_ 586 | #define metamacro_if_eq_recursive0_5(...) metamacro_expand_ 587 | #define metamacro_if_eq_recursive0_6(...) metamacro_expand_ 588 | #define metamacro_if_eq_recursive0_7(...) metamacro_expand_ 589 | #define metamacro_if_eq_recursive0_8(...) metamacro_expand_ 590 | #define metamacro_if_eq_recursive0_9(...) metamacro_expand_ 591 | #define metamacro_if_eq_recursive0_10(...) metamacro_expand_ 592 | #define metamacro_if_eq_recursive0_11(...) metamacro_expand_ 593 | #define metamacro_if_eq_recursive0_12(...) metamacro_expand_ 594 | #define metamacro_if_eq_recursive0_13(...) metamacro_expand_ 595 | #define metamacro_if_eq_recursive0_14(...) metamacro_expand_ 596 | #define metamacro_if_eq_recursive0_15(...) metamacro_expand_ 597 | #define metamacro_if_eq_recursive0_16(...) metamacro_expand_ 598 | #define metamacro_if_eq_recursive0_17(...) metamacro_expand_ 599 | #define metamacro_if_eq_recursive0_18(...) metamacro_expand_ 600 | #define metamacro_if_eq_recursive0_19(...) metamacro_expand_ 601 | #define metamacro_if_eq_recursive0_20(...) metamacro_expand_ 602 | 603 | #define metamacro_if_eq_recursive1(VALUE) metamacro_if_eq_recursive0(metamacro_dec(VALUE)) 604 | #define metamacro_if_eq_recursive2(VALUE) metamacro_if_eq_recursive1(metamacro_dec(VALUE)) 605 | #define metamacro_if_eq_recursive3(VALUE) metamacro_if_eq_recursive2(metamacro_dec(VALUE)) 606 | #define metamacro_if_eq_recursive4(VALUE) metamacro_if_eq_recursive3(metamacro_dec(VALUE)) 607 | #define metamacro_if_eq_recursive5(VALUE) metamacro_if_eq_recursive4(metamacro_dec(VALUE)) 608 | #define metamacro_if_eq_recursive6(VALUE) metamacro_if_eq_recursive5(metamacro_dec(VALUE)) 609 | #define metamacro_if_eq_recursive7(VALUE) metamacro_if_eq_recursive6(metamacro_dec(VALUE)) 610 | #define metamacro_if_eq_recursive8(VALUE) metamacro_if_eq_recursive7(metamacro_dec(VALUE)) 611 | #define metamacro_if_eq_recursive9(VALUE) metamacro_if_eq_recursive8(metamacro_dec(VALUE)) 612 | #define metamacro_if_eq_recursive10(VALUE) metamacro_if_eq_recursive9(metamacro_dec(VALUE)) 613 | #define metamacro_if_eq_recursive11(VALUE) metamacro_if_eq_recursive10(metamacro_dec(VALUE)) 614 | #define metamacro_if_eq_recursive12(VALUE) metamacro_if_eq_recursive11(metamacro_dec(VALUE)) 615 | #define metamacro_if_eq_recursive13(VALUE) metamacro_if_eq_recursive12(metamacro_dec(VALUE)) 616 | #define metamacro_if_eq_recursive14(VALUE) metamacro_if_eq_recursive13(metamacro_dec(VALUE)) 617 | #define metamacro_if_eq_recursive15(VALUE) metamacro_if_eq_recursive14(metamacro_dec(VALUE)) 618 | #define metamacro_if_eq_recursive16(VALUE) metamacro_if_eq_recursive15(metamacro_dec(VALUE)) 619 | #define metamacro_if_eq_recursive17(VALUE) metamacro_if_eq_recursive16(metamacro_dec(VALUE)) 620 | #define metamacro_if_eq_recursive18(VALUE) metamacro_if_eq_recursive17(metamacro_dec(VALUE)) 621 | #define metamacro_if_eq_recursive19(VALUE) metamacro_if_eq_recursive18(metamacro_dec(VALUE)) 622 | #define metamacro_if_eq_recursive20(VALUE) metamacro_if_eq_recursive19(metamacro_dec(VALUE)) 623 | 624 | // metamacro_take expansions 625 | #define metamacro_take0(...) 626 | #define metamacro_take1(...) metamacro_head(__VA_ARGS__) 627 | #define metamacro_take2(...) metamacro_head(__VA_ARGS__), metamacro_take1(metamacro_tail(__VA_ARGS__)) 628 | #define metamacro_take3(...) metamacro_head(__VA_ARGS__), metamacro_take2(metamacro_tail(__VA_ARGS__)) 629 | #define metamacro_take4(...) metamacro_head(__VA_ARGS__), metamacro_take3(metamacro_tail(__VA_ARGS__)) 630 | #define metamacro_take5(...) metamacro_head(__VA_ARGS__), metamacro_take4(metamacro_tail(__VA_ARGS__)) 631 | #define metamacro_take6(...) metamacro_head(__VA_ARGS__), metamacro_take5(metamacro_tail(__VA_ARGS__)) 632 | #define metamacro_take7(...) metamacro_head(__VA_ARGS__), metamacro_take6(metamacro_tail(__VA_ARGS__)) 633 | #define metamacro_take8(...) metamacro_head(__VA_ARGS__), metamacro_take7(metamacro_tail(__VA_ARGS__)) 634 | #define metamacro_take9(...) metamacro_head(__VA_ARGS__), metamacro_take8(metamacro_tail(__VA_ARGS__)) 635 | #define metamacro_take10(...) metamacro_head(__VA_ARGS__), metamacro_take9(metamacro_tail(__VA_ARGS__)) 636 | #define metamacro_take11(...) metamacro_head(__VA_ARGS__), metamacro_take10(metamacro_tail(__VA_ARGS__)) 637 | #define metamacro_take12(...) metamacro_head(__VA_ARGS__), metamacro_take11(metamacro_tail(__VA_ARGS__)) 638 | #define metamacro_take13(...) metamacro_head(__VA_ARGS__), metamacro_take12(metamacro_tail(__VA_ARGS__)) 639 | #define metamacro_take14(...) metamacro_head(__VA_ARGS__), metamacro_take13(metamacro_tail(__VA_ARGS__)) 640 | #define metamacro_take15(...) metamacro_head(__VA_ARGS__), metamacro_take14(metamacro_tail(__VA_ARGS__)) 641 | #define metamacro_take16(...) metamacro_head(__VA_ARGS__), metamacro_take15(metamacro_tail(__VA_ARGS__)) 642 | #define metamacro_take17(...) metamacro_head(__VA_ARGS__), metamacro_take16(metamacro_tail(__VA_ARGS__)) 643 | #define metamacro_take18(...) metamacro_head(__VA_ARGS__), metamacro_take17(metamacro_tail(__VA_ARGS__)) 644 | #define metamacro_take19(...) metamacro_head(__VA_ARGS__), metamacro_take18(metamacro_tail(__VA_ARGS__)) 645 | #define metamacro_take20(...) metamacro_head(__VA_ARGS__), metamacro_take19(metamacro_tail(__VA_ARGS__)) 646 | 647 | // metamacro_drop expansions 648 | #define metamacro_drop0(...) __VA_ARGS__ 649 | #define metamacro_drop1(...) metamacro_tail(__VA_ARGS__) 650 | #define metamacro_drop2(...) metamacro_drop1(metamacro_tail(__VA_ARGS__)) 651 | #define metamacro_drop3(...) metamacro_drop2(metamacro_tail(__VA_ARGS__)) 652 | #define metamacro_drop4(...) metamacro_drop3(metamacro_tail(__VA_ARGS__)) 653 | #define metamacro_drop5(...) metamacro_drop4(metamacro_tail(__VA_ARGS__)) 654 | #define metamacro_drop6(...) metamacro_drop5(metamacro_tail(__VA_ARGS__)) 655 | #define metamacro_drop7(...) metamacro_drop6(metamacro_tail(__VA_ARGS__)) 656 | #define metamacro_drop8(...) metamacro_drop7(metamacro_tail(__VA_ARGS__)) 657 | #define metamacro_drop9(...) metamacro_drop8(metamacro_tail(__VA_ARGS__)) 658 | #define metamacro_drop10(...) metamacro_drop9(metamacro_tail(__VA_ARGS__)) 659 | #define metamacro_drop11(...) metamacro_drop10(metamacro_tail(__VA_ARGS__)) 660 | #define metamacro_drop12(...) metamacro_drop11(metamacro_tail(__VA_ARGS__)) 661 | #define metamacro_drop13(...) metamacro_drop12(metamacro_tail(__VA_ARGS__)) 662 | #define metamacro_drop14(...) metamacro_drop13(metamacro_tail(__VA_ARGS__)) 663 | #define metamacro_drop15(...) metamacro_drop14(metamacro_tail(__VA_ARGS__)) 664 | #define metamacro_drop16(...) metamacro_drop15(metamacro_tail(__VA_ARGS__)) 665 | #define metamacro_drop17(...) metamacro_drop16(metamacro_tail(__VA_ARGS__)) 666 | #define metamacro_drop18(...) metamacro_drop17(metamacro_tail(__VA_ARGS__)) 667 | #define metamacro_drop19(...) metamacro_drop18(metamacro_tail(__VA_ARGS__)) 668 | #define metamacro_drop20(...) metamacro_drop19(metamacro_tail(__VA_ARGS__)) 669 | 670 | #endif 671 | -------------------------------------------------------------------------------- /KVOBlockBindingExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 718CC05D139D08D000A56984 /* NSObject+KVOBlockBinding.m in Sources */ = {isa = PBXBuildFile; fileRef = 718CC05C139D08D000A56984 /* NSObject+KVOBlockBinding.m */; }; 11 | 718CC05E139D08D000A56984 /* NSObject+KVOBlockBinding.m in Sources */ = {isa = PBXBuildFile; fileRef = 718CC05C139D08D000A56984 /* NSObject+KVOBlockBinding.m */; }; 12 | 71FB1A671384C7EB00CEA223 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 71FB1A661384C7EB00CEA223 /* UIKit.framework */; }; 13 | 71FB1A691384C7EB00CEA223 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 71FB1A681384C7EB00CEA223 /* Foundation.framework */; }; 14 | 71FB1A6B1384C7EB00CEA223 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 71FB1A6A1384C7EB00CEA223 /* CoreGraphics.framework */; }; 15 | 71FB1A871384C7EC00CEA223 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 71FB1A661384C7EB00CEA223 /* UIKit.framework */; }; 16 | 71FB1A881384C7EC00CEA223 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 71FB1A681384C7EB00CEA223 /* Foundation.framework */; }; 17 | 71FB1A891384C7EC00CEA223 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 71FB1A6A1384C7EB00CEA223 /* CoreGraphics.framework */; }; 18 | 71FB1A911384C7ED00CEA223 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 71FB1A8F1384C7ED00CEA223 /* InfoPlist.strings */; }; 19 | 71FB1A941384C7ED00CEA223 /* KVOBlockBindingTests.h in Resources */ = {isa = PBXBuildFile; fileRef = 71FB1A931384C7ED00CEA223 /* KVOBlockBindingTests.h */; }; 20 | 71FB1A961384C7ED00CEA223 /* KVOBlockBindingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 71FB1A951384C7ED00CEA223 /* KVOBlockBindingTests.m */; }; 21 | 71FB1AB71384C96400CEA223 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 71FB1AA61384C96400CEA223 /* InfoPlist.strings */; }; 22 | 71FB1AB81384C96400CEA223 /* KVOBlockBindingViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 71FB1AA81384C96400CEA223 /* KVOBlockBindingViewController.xib */; }; 23 | 71FB1AB91384C96400CEA223 /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 71FB1AAA1384C96400CEA223 /* MainWindow.xib */; }; 24 | 71FB1ABA1384C96400CEA223 /* ExampleModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 71FB1AAD1384C96400CEA223 /* ExampleModel.m */; }; 25 | 71FB1ABC1384C96400CEA223 /* KVOBlockBindingAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 71FB1AB11384C96400CEA223 /* KVOBlockBindingAppDelegate.m */; }; 26 | 71FB1ABD1384C96400CEA223 /* KVOBlockBindingViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 71FB1AB31384C96400CEA223 /* KVOBlockBindingViewController.m */; }; 27 | 71FB1ABE1384C96400CEA223 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 71FB1AB41384C96400CEA223 /* main.m */; }; 28 | /* End PBXBuildFile section */ 29 | 30 | /* Begin PBXContainerItemProxy section */ 31 | 71FB1A8A1384C7EC00CEA223 /* PBXContainerItemProxy */ = { 32 | isa = PBXContainerItemProxy; 33 | containerPortal = 71FB1A591384C7EA00CEA223 /* Project object */; 34 | proxyType = 1; 35 | remoteGlobalIDString = 71FB1A611384C7EB00CEA223; 36 | remoteInfo = KVOBlockBinding; 37 | }; 38 | /* End PBXContainerItemProxy section */ 39 | 40 | /* Begin PBXFileReference section */ 41 | 718CC05B139D08D000A56984 /* NSObject+KVOBlockBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+KVOBlockBinding.h"; sourceTree = ""; }; 42 | 718CC05C139D08D000A56984 /* NSObject+KVOBlockBinding.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+KVOBlockBinding.m"; sourceTree = ""; }; 43 | 71FB1A621384C7EB00CEA223 /* KVOBlockBindingExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KVOBlockBindingExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 44 | 71FB1A661384C7EB00CEA223 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 45 | 71FB1A681384C7EB00CEA223 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 46 | 71FB1A6A1384C7EB00CEA223 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 47 | 71FB1A861384C7EC00CEA223 /* KVOBlockBindingTests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = KVOBlockBindingTests.octest; sourceTree = BUILT_PRODUCTS_DIR; }; 48 | 71FB1A8E1384C7ED00CEA223 /* KVOBlockBindingTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "KVOBlockBindingTests-Info.plist"; sourceTree = ""; }; 49 | 71FB1A901384C7ED00CEA223 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 50 | 71FB1A921384C7ED00CEA223 /* KVOBlockBindingTests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "KVOBlockBindingTests-Prefix.pch"; sourceTree = ""; }; 51 | 71FB1A931384C7ED00CEA223 /* KVOBlockBindingTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KVOBlockBindingTests.h; sourceTree = ""; }; 52 | 71FB1A951384C7ED00CEA223 /* KVOBlockBindingTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KVOBlockBindingTests.m; sourceTree = ""; }; 53 | 71FB1AA71384C96400CEA223 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 54 | 71FB1AA91384C96400CEA223 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/KVOBlockBindingViewController.xib; sourceTree = ""; }; 55 | 71FB1AAB1384C96400CEA223 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainWindow.xib; sourceTree = ""; }; 56 | 71FB1AAC1384C96400CEA223 /* ExampleModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExampleModel.h; sourceTree = ""; }; 57 | 71FB1AAD1384C96400CEA223 /* ExampleModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ExampleModel.m; sourceTree = ""; }; 58 | 71FB1AAE1384C96400CEA223 /* KVOBlockBinding-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "KVOBlockBinding-Info.plist"; sourceTree = ""; }; 59 | 71FB1AAF1384C96400CEA223 /* KVOBlockBinding-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "KVOBlockBinding-Prefix.pch"; sourceTree = ""; }; 60 | 71FB1AB01384C96400CEA223 /* KVOBlockBindingAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KVOBlockBindingAppDelegate.h; sourceTree = ""; }; 61 | 71FB1AB11384C96400CEA223 /* KVOBlockBindingAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KVOBlockBindingAppDelegate.m; sourceTree = ""; }; 62 | 71FB1AB21384C96400CEA223 /* KVOBlockBindingViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KVOBlockBindingViewController.h; sourceTree = ""; }; 63 | 71FB1AB31384C96400CEA223 /* KVOBlockBindingViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KVOBlockBindingViewController.m; sourceTree = ""; }; 64 | 71FB1AB41384C96400CEA223 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 65 | /* End PBXFileReference section */ 66 | 67 | /* Begin PBXFrameworksBuildPhase section */ 68 | 71FB1A5F1384C7EB00CEA223 /* Frameworks */ = { 69 | isa = PBXFrameworksBuildPhase; 70 | buildActionMask = 2147483647; 71 | files = ( 72 | 71FB1A671384C7EB00CEA223 /* UIKit.framework in Frameworks */, 73 | 71FB1A691384C7EB00CEA223 /* Foundation.framework in Frameworks */, 74 | 71FB1A6B1384C7EB00CEA223 /* CoreGraphics.framework in Frameworks */, 75 | ); 76 | runOnlyForDeploymentPostprocessing = 0; 77 | }; 78 | 71FB1A821384C7EC00CEA223 /* Frameworks */ = { 79 | isa = PBXFrameworksBuildPhase; 80 | buildActionMask = 2147483647; 81 | files = ( 82 | 71FB1A871384C7EC00CEA223 /* UIKit.framework in Frameworks */, 83 | 71FB1A881384C7EC00CEA223 /* Foundation.framework in Frameworks */, 84 | 71FB1A891384C7EC00CEA223 /* CoreGraphics.framework in Frameworks */, 85 | ); 86 | runOnlyForDeploymentPostprocessing = 0; 87 | }; 88 | /* End PBXFrameworksBuildPhase section */ 89 | 90 | /* Begin PBXGroup section */ 91 | 71FB1A571384C7EA00CEA223 = { 92 | isa = PBXGroup; 93 | children = ( 94 | 71FB1AC01384C97300CEA223 /* KVOBlockBinding */, 95 | 71FB1AA51384C96400CEA223 /* KVOBlockBindingExample */, 96 | 71FB1A8C1384C7EC00CEA223 /* KVOBlockBindingTests */, 97 | 71FB1A651384C7EB00CEA223 /* Frameworks */, 98 | 71FB1A631384C7EB00CEA223 /* Products */, 99 | ); 100 | sourceTree = ""; 101 | }; 102 | 71FB1A631384C7EB00CEA223 /* Products */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 71FB1A621384C7EB00CEA223 /* KVOBlockBindingExample.app */, 106 | 71FB1A861384C7EC00CEA223 /* KVOBlockBindingTests.octest */, 107 | ); 108 | name = Products; 109 | sourceTree = ""; 110 | }; 111 | 71FB1A651384C7EB00CEA223 /* Frameworks */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | 71FB1A661384C7EB00CEA223 /* UIKit.framework */, 115 | 71FB1A681384C7EB00CEA223 /* Foundation.framework */, 116 | 71FB1A6A1384C7EB00CEA223 /* CoreGraphics.framework */, 117 | ); 118 | name = Frameworks; 119 | sourceTree = ""; 120 | }; 121 | 71FB1A8C1384C7EC00CEA223 /* KVOBlockBindingTests */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | 71FB1A931384C7ED00CEA223 /* KVOBlockBindingTests.h */, 125 | 71FB1A951384C7ED00CEA223 /* KVOBlockBindingTests.m */, 126 | 71FB1A8D1384C7ED00CEA223 /* Supporting Files */, 127 | ); 128 | path = KVOBlockBindingTests; 129 | sourceTree = ""; 130 | }; 131 | 71FB1A8D1384C7ED00CEA223 /* Supporting Files */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | 71FB1A8E1384C7ED00CEA223 /* KVOBlockBindingTests-Info.plist */, 135 | 71FB1A8F1384C7ED00CEA223 /* InfoPlist.strings */, 136 | 71FB1A921384C7ED00CEA223 /* KVOBlockBindingTests-Prefix.pch */, 137 | ); 138 | name = "Supporting Files"; 139 | sourceTree = ""; 140 | }; 141 | 71FB1AA51384C96400CEA223 /* KVOBlockBindingExample */ = { 142 | isa = PBXGroup; 143 | children = ( 144 | 71FB1AA61384C96400CEA223 /* InfoPlist.strings */, 145 | 71FB1AA81384C96400CEA223 /* KVOBlockBindingViewController.xib */, 146 | 71FB1AAA1384C96400CEA223 /* MainWindow.xib */, 147 | 71FB1AAC1384C96400CEA223 /* ExampleModel.h */, 148 | 71FB1AAD1384C96400CEA223 /* ExampleModel.m */, 149 | 71FB1AAE1384C96400CEA223 /* KVOBlockBinding-Info.plist */, 150 | 71FB1AAF1384C96400CEA223 /* KVOBlockBinding-Prefix.pch */, 151 | 71FB1AB01384C96400CEA223 /* KVOBlockBindingAppDelegate.h */, 152 | 71FB1AB11384C96400CEA223 /* KVOBlockBindingAppDelegate.m */, 153 | 71FB1AB21384C96400CEA223 /* KVOBlockBindingViewController.h */, 154 | 71FB1AB31384C96400CEA223 /* KVOBlockBindingViewController.m */, 155 | 71FB1AB41384C96400CEA223 /* main.m */, 156 | ); 157 | path = KVOBlockBindingExample; 158 | sourceTree = ""; 159 | }; 160 | 71FB1AC01384C97300CEA223 /* KVOBlockBinding */ = { 161 | isa = PBXGroup; 162 | children = ( 163 | 718CC05B139D08D000A56984 /* NSObject+KVOBlockBinding.h */, 164 | 718CC05C139D08D000A56984 /* NSObject+KVOBlockBinding.m */, 165 | ); 166 | path = KVOBlockBinding; 167 | sourceTree = ""; 168 | }; 169 | /* End PBXGroup section */ 170 | 171 | /* Begin PBXNativeTarget section */ 172 | 71FB1A611384C7EB00CEA223 /* KVOBlockBindingExample */ = { 173 | isa = PBXNativeTarget; 174 | buildConfigurationList = 71FB1A991384C7ED00CEA223 /* Build configuration list for PBXNativeTarget "KVOBlockBindingExample" */; 175 | buildPhases = ( 176 | 71FB1A5E1384C7EB00CEA223 /* Sources */, 177 | 71FB1A5F1384C7EB00CEA223 /* Frameworks */, 178 | 71FB1A601384C7EB00CEA223 /* Resources */, 179 | ); 180 | buildRules = ( 181 | ); 182 | dependencies = ( 183 | ); 184 | name = KVOBlockBindingExample; 185 | productName = KVOBlockBinding; 186 | productReference = 71FB1A621384C7EB00CEA223 /* KVOBlockBindingExample.app */; 187 | productType = "com.apple.product-type.application"; 188 | }; 189 | 71FB1A851384C7EC00CEA223 /* KVOBlockBindingTests */ = { 190 | isa = PBXNativeTarget; 191 | buildConfigurationList = 71FB1A9C1384C7ED00CEA223 /* Build configuration list for PBXNativeTarget "KVOBlockBindingTests" */; 192 | buildPhases = ( 193 | 71FB1A811384C7EC00CEA223 /* Sources */, 194 | 71FB1A821384C7EC00CEA223 /* Frameworks */, 195 | 71FB1A831384C7EC00CEA223 /* Resources */, 196 | 71FB1A841384C7EC00CEA223 /* ShellScript */, 197 | ); 198 | buildRules = ( 199 | ); 200 | dependencies = ( 201 | 71FB1A8B1384C7EC00CEA223 /* PBXTargetDependency */, 202 | ); 203 | name = KVOBlockBindingTests; 204 | productName = KVOBlockBindingTests; 205 | productReference = 71FB1A861384C7EC00CEA223 /* KVOBlockBindingTests.octest */; 206 | productType = "com.apple.product-type.bundle"; 207 | }; 208 | /* End PBXNativeTarget section */ 209 | 210 | /* Begin PBXProject section */ 211 | 71FB1A591384C7EA00CEA223 /* Project object */ = { 212 | isa = PBXProject; 213 | buildConfigurationList = 71FB1A5C1384C7EA00CEA223 /* Build configuration list for PBXProject "KVOBlockBindingExample" */; 214 | compatibilityVersion = "Xcode 3.2"; 215 | developmentRegion = English; 216 | hasScannedForEncodings = 0; 217 | knownRegions = ( 218 | en, 219 | ); 220 | mainGroup = 71FB1A571384C7EA00CEA223; 221 | productRefGroup = 71FB1A631384C7EB00CEA223 /* Products */; 222 | projectDirPath = ""; 223 | projectRoot = ""; 224 | targets = ( 225 | 71FB1A611384C7EB00CEA223 /* KVOBlockBindingExample */, 226 | 71FB1A851384C7EC00CEA223 /* KVOBlockBindingTests */, 227 | ); 228 | }; 229 | /* End PBXProject section */ 230 | 231 | /* Begin PBXResourcesBuildPhase section */ 232 | 71FB1A601384C7EB00CEA223 /* Resources */ = { 233 | isa = PBXResourcesBuildPhase; 234 | buildActionMask = 2147483647; 235 | files = ( 236 | 71FB1AB71384C96400CEA223 /* InfoPlist.strings in Resources */, 237 | 71FB1AB81384C96400CEA223 /* KVOBlockBindingViewController.xib in Resources */, 238 | 71FB1AB91384C96400CEA223 /* MainWindow.xib in Resources */, 239 | ); 240 | runOnlyForDeploymentPostprocessing = 0; 241 | }; 242 | 71FB1A831384C7EC00CEA223 /* Resources */ = { 243 | isa = PBXResourcesBuildPhase; 244 | buildActionMask = 2147483647; 245 | files = ( 246 | 71FB1A911384C7ED00CEA223 /* InfoPlist.strings in Resources */, 247 | 71FB1A941384C7ED00CEA223 /* KVOBlockBindingTests.h in Resources */, 248 | ); 249 | runOnlyForDeploymentPostprocessing = 0; 250 | }; 251 | /* End PBXResourcesBuildPhase section */ 252 | 253 | /* Begin PBXShellScriptBuildPhase section */ 254 | 71FB1A841384C7EC00CEA223 /* ShellScript */ = { 255 | isa = PBXShellScriptBuildPhase; 256 | buildActionMask = 2147483647; 257 | files = ( 258 | ); 259 | inputPaths = ( 260 | ); 261 | outputPaths = ( 262 | ); 263 | runOnlyForDeploymentPostprocessing = 0; 264 | shellPath = /bin/sh; 265 | shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n"; 266 | }; 267 | /* End PBXShellScriptBuildPhase section */ 268 | 269 | /* Begin PBXSourcesBuildPhase section */ 270 | 71FB1A5E1384C7EB00CEA223 /* Sources */ = { 271 | isa = PBXSourcesBuildPhase; 272 | buildActionMask = 2147483647; 273 | files = ( 274 | 71FB1ABA1384C96400CEA223 /* ExampleModel.m in Sources */, 275 | 71FB1ABC1384C96400CEA223 /* KVOBlockBindingAppDelegate.m in Sources */, 276 | 71FB1ABD1384C96400CEA223 /* KVOBlockBindingViewController.m in Sources */, 277 | 71FB1ABE1384C96400CEA223 /* main.m in Sources */, 278 | 718CC05D139D08D000A56984 /* NSObject+KVOBlockBinding.m in Sources */, 279 | ); 280 | runOnlyForDeploymentPostprocessing = 0; 281 | }; 282 | 71FB1A811384C7EC00CEA223 /* Sources */ = { 283 | isa = PBXSourcesBuildPhase; 284 | buildActionMask = 2147483647; 285 | files = ( 286 | 71FB1A961384C7ED00CEA223 /* KVOBlockBindingTests.m in Sources */, 287 | 718CC05E139D08D000A56984 /* NSObject+KVOBlockBinding.m in Sources */, 288 | ); 289 | runOnlyForDeploymentPostprocessing = 0; 290 | }; 291 | /* End PBXSourcesBuildPhase section */ 292 | 293 | /* Begin PBXTargetDependency section */ 294 | 71FB1A8B1384C7EC00CEA223 /* PBXTargetDependency */ = { 295 | isa = PBXTargetDependency; 296 | target = 71FB1A611384C7EB00CEA223 /* KVOBlockBindingExample */; 297 | targetProxy = 71FB1A8A1384C7EC00CEA223 /* PBXContainerItemProxy */; 298 | }; 299 | /* End PBXTargetDependency section */ 300 | 301 | /* Begin PBXVariantGroup section */ 302 | 71FB1A8F1384C7ED00CEA223 /* InfoPlist.strings */ = { 303 | isa = PBXVariantGroup; 304 | children = ( 305 | 71FB1A901384C7ED00CEA223 /* en */, 306 | ); 307 | name = InfoPlist.strings; 308 | sourceTree = ""; 309 | }; 310 | 71FB1AA61384C96400CEA223 /* InfoPlist.strings */ = { 311 | isa = PBXVariantGroup; 312 | children = ( 313 | 71FB1AA71384C96400CEA223 /* en */, 314 | ); 315 | name = InfoPlist.strings; 316 | sourceTree = ""; 317 | }; 318 | 71FB1AA81384C96400CEA223 /* KVOBlockBindingViewController.xib */ = { 319 | isa = PBXVariantGroup; 320 | children = ( 321 | 71FB1AA91384C96400CEA223 /* en */, 322 | ); 323 | name = KVOBlockBindingViewController.xib; 324 | sourceTree = ""; 325 | }; 326 | 71FB1AAA1384C96400CEA223 /* MainWindow.xib */ = { 327 | isa = PBXVariantGroup; 328 | children = ( 329 | 71FB1AAB1384C96400CEA223 /* en */, 330 | ); 331 | name = MainWindow.xib; 332 | sourceTree = ""; 333 | }; 334 | /* End PBXVariantGroup section */ 335 | 336 | /* Begin XCBuildConfiguration section */ 337 | 71FB1A971384C7ED00CEA223 /* Debug */ = { 338 | isa = XCBuildConfiguration; 339 | buildSettings = { 340 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 341 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 342 | GCC_C_LANGUAGE_STANDARD = gnu99; 343 | GCC_OPTIMIZATION_LEVEL = 0; 344 | GCC_PREPROCESSOR_DEFINITIONS = DEBUG; 345 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 346 | GCC_VERSION = com.apple.compilers.llvmgcc42; 347 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 348 | GCC_WARN_UNUSED_VARIABLE = YES; 349 | IPHONEOS_DEPLOYMENT_TARGET = 4.3; 350 | SDKROOT = iphoneos; 351 | }; 352 | name = Debug; 353 | }; 354 | 71FB1A981384C7ED00CEA223 /* Release */ = { 355 | isa = XCBuildConfiguration; 356 | buildSettings = { 357 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 358 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 359 | GCC_C_LANGUAGE_STANDARD = gnu99; 360 | GCC_VERSION = com.apple.compilers.llvmgcc42; 361 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 362 | GCC_WARN_UNUSED_VARIABLE = YES; 363 | IPHONEOS_DEPLOYMENT_TARGET = 4.3; 364 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 365 | SDKROOT = iphoneos; 366 | }; 367 | name = Release; 368 | }; 369 | 71FB1A9A1384C7ED00CEA223 /* Debug */ = { 370 | isa = XCBuildConfiguration; 371 | buildSettings = { 372 | ALWAYS_SEARCH_USER_PATHS = NO; 373 | COPY_PHASE_STRIP = NO; 374 | GCC_DYNAMIC_NO_PIC = NO; 375 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 376 | GCC_PREFIX_HEADER = "KVOBlockBindingExample/KVOBlockBinding-Prefix.pch"; 377 | INFOPLIST_FILE = "KVOBlockBindingExample/KVOBlockBinding-Info.plist"; 378 | PRODUCT_NAME = "$(TARGET_NAME)"; 379 | WRAPPER_EXTENSION = app; 380 | }; 381 | name = Debug; 382 | }; 383 | 71FB1A9B1384C7ED00CEA223 /* Release */ = { 384 | isa = XCBuildConfiguration; 385 | buildSettings = { 386 | ALWAYS_SEARCH_USER_PATHS = NO; 387 | COPY_PHASE_STRIP = YES; 388 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 389 | GCC_PREFIX_HEADER = "KVOBlockBindingExample/KVOBlockBinding-Prefix.pch"; 390 | INFOPLIST_FILE = "KVOBlockBindingExample/KVOBlockBinding-Info.plist"; 391 | PRODUCT_NAME = "$(TARGET_NAME)"; 392 | VALIDATE_PRODUCT = YES; 393 | WRAPPER_EXTENSION = app; 394 | }; 395 | name = Release; 396 | }; 397 | 71FB1A9D1384C7ED00CEA223 /* Debug */ = { 398 | isa = XCBuildConfiguration; 399 | buildSettings = { 400 | ALWAYS_SEARCH_USER_PATHS = NO; 401 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/KVOBlockBindingExample.app/KVOBlockBindingExample"; 402 | FRAMEWORK_SEARCH_PATHS = ( 403 | "$(SDKROOT)/Developer/Library/Frameworks", 404 | "$(DEVELOPER_LIBRARY_DIR)/Frameworks", 405 | ); 406 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 407 | GCC_PREFIX_HEADER = "KVOBlockBindingTests/KVOBlockBindingTests-Prefix.pch"; 408 | INFOPLIST_FILE = "KVOBlockBindingTests/KVOBlockBindingTests-Info.plist"; 409 | OTHER_LDFLAGS = ( 410 | "-framework", 411 | SenTestingKit, 412 | ); 413 | PRODUCT_NAME = "$(TARGET_NAME)"; 414 | TEST_HOST = "$(BUNDLE_LOADER)"; 415 | WRAPPER_EXTENSION = octest; 416 | }; 417 | name = Debug; 418 | }; 419 | 71FB1A9E1384C7ED00CEA223 /* Release */ = { 420 | isa = XCBuildConfiguration; 421 | buildSettings = { 422 | ALWAYS_SEARCH_USER_PATHS = NO; 423 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/KVOBlockBindingExample.app/KVOBlockBindingExample"; 424 | FRAMEWORK_SEARCH_PATHS = ( 425 | "$(SDKROOT)/Developer/Library/Frameworks", 426 | "$(DEVELOPER_LIBRARY_DIR)/Frameworks", 427 | ); 428 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 429 | GCC_PREFIX_HEADER = "KVOBlockBindingTests/KVOBlockBindingTests-Prefix.pch"; 430 | INFOPLIST_FILE = "KVOBlockBindingTests/KVOBlockBindingTests-Info.plist"; 431 | OTHER_LDFLAGS = ( 432 | "-framework", 433 | SenTestingKit, 434 | ); 435 | PRODUCT_NAME = "$(TARGET_NAME)"; 436 | TEST_HOST = "$(BUNDLE_LOADER)"; 437 | WRAPPER_EXTENSION = octest; 438 | }; 439 | name = Release; 440 | }; 441 | /* End XCBuildConfiguration section */ 442 | 443 | /* Begin XCConfigurationList section */ 444 | 71FB1A5C1384C7EA00CEA223 /* Build configuration list for PBXProject "KVOBlockBindingExample" */ = { 445 | isa = XCConfigurationList; 446 | buildConfigurations = ( 447 | 71FB1A971384C7ED00CEA223 /* Debug */, 448 | 71FB1A981384C7ED00CEA223 /* Release */, 449 | ); 450 | defaultConfigurationIsVisible = 0; 451 | defaultConfigurationName = Release; 452 | }; 453 | 71FB1A991384C7ED00CEA223 /* Build configuration list for PBXNativeTarget "KVOBlockBindingExample" */ = { 454 | isa = XCConfigurationList; 455 | buildConfigurations = ( 456 | 71FB1A9A1384C7ED00CEA223 /* Debug */, 457 | 71FB1A9B1384C7ED00CEA223 /* Release */, 458 | ); 459 | defaultConfigurationIsVisible = 0; 460 | defaultConfigurationName = Release; 461 | }; 462 | 71FB1A9C1384C7ED00CEA223 /* Build configuration list for PBXNativeTarget "KVOBlockBindingTests" */ = { 463 | isa = XCConfigurationList; 464 | buildConfigurations = ( 465 | 71FB1A9D1384C7ED00CEA223 /* Debug */, 466 | 71FB1A9E1384C7ED00CEA223 /* Release */, 467 | ); 468 | defaultConfigurationIsVisible = 0; 469 | defaultConfigurationName = Release; 470 | }; 471 | /* End XCConfigurationList section */ 472 | }; 473 | rootObject = 71FB1A591384C7EA00CEA223 /* Project object */; 474 | } 475 | -------------------------------------------------------------------------------- /KVOBlockBindingExample/ExampleModel.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | 4 | @interface ExampleModel : NSObject 5 | 6 | @property (nonatomic, assign) NSInteger exampleValue1; 7 | @property (nonatomic, assign) NSInteger exampleValue2; 8 | 9 | @end 10 | -------------------------------------------------------------------------------- /KVOBlockBindingExample/ExampleModel.m: -------------------------------------------------------------------------------- 1 | #import "ExampleModel.h" 2 | 3 | 4 | @implementation ExampleModel 5 | @synthesize exampleValue1; 6 | @synthesize exampleValue2; 7 | @end 8 | -------------------------------------------------------------------------------- /KVOBlockBindingExample/KVOBlockBinding-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIconFile 12 | 13 | CFBundleIdentifier 14 | au.com.rayh.${PRODUCT_NAME:rfc1034identifier} 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | ${PRODUCT_NAME} 19 | CFBundlePackageType 20 | APPL 21 | CFBundleShortVersionString 22 | 1.0 23 | CFBundleSignature 24 | ???? 25 | CFBundleVersion 26 | 1.0 27 | LSRequiresIPhoneOS 28 | 29 | NSMainNibFile 30 | MainWindow 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /KVOBlockBindingExample/KVOBlockBinding-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'KVOBlockBinding' target in the 'KVOBlockBinding' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_3_0 8 | #warning "This project uses features only available in iPhone SDK 3.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #import "NSObject+KVOBlockBinding.h" 15 | #endif 16 | -------------------------------------------------------------------------------- /KVOBlockBindingExample/KVOBlockBindingAppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @class KVOBlockBindingViewController; 4 | 5 | @interface KVOBlockBindingAppDelegate : NSObject { 6 | 7 | } 8 | 9 | @property (nonatomic, retain) IBOutlet UIWindow *window; 10 | 11 | @property (nonatomic, retain) IBOutlet KVOBlockBindingViewController *viewController; 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /KVOBlockBindingExample/KVOBlockBindingAppDelegate.m: -------------------------------------------------------------------------------- 1 | #import "KVOBlockBindingAppDelegate.h" 2 | 3 | #import "KVOBlockBindingViewController.h" 4 | 5 | @implementation KVOBlockBindingAppDelegate 6 | 7 | 8 | @synthesize window=_window; 9 | 10 | @synthesize viewController=_viewController; 11 | 12 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 13 | { 14 | // Override point for customization after application launch. 15 | 16 | self.window.rootViewController = self.viewController; 17 | [self.window makeKeyAndVisible]; 18 | return YES; 19 | } 20 | 21 | - (void)dealloc 22 | { 23 | [_window release]; 24 | [_viewController release]; 25 | [super dealloc]; 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /KVOBlockBindingExample/KVOBlockBindingViewController.h: -------------------------------------------------------------------------------- 1 | #import 2 | @class ExampleModel; 3 | @class KVOBlockBinding; 4 | 5 | @interface KVOBlockBindingViewController : UIViewController { 6 | IBOutlet UILabel *label1; 7 | IBOutlet UILabel *label2; 8 | IBOutlet UIButton *toggleObservingValue1Button; 9 | IBOutlet UIButton *toggleObservingValue2Button; 10 | IBOutlet UIButton *removeObservingAllButton; 11 | } 12 | 13 | @property (nonatomic, retain) ExampleModel *model; 14 | @property (retain, nonatomic) IBOutlet UIStepper *stepper1; 15 | @property (retain, nonatomic) IBOutlet UIStepper *stepper2; 16 | @property (retain, nonatomic) IBOutlet UILabel *stepper1Label; 17 | @property (retain, nonatomic) IBOutlet UILabel *stepper2Label; 18 | 19 | -(IBAction)pressMeButtonPressed:(id)sender; 20 | -(IBAction)toggleObservingValue2ButtonPressed:(id)sender; 21 | -(IBAction)toggleObservingValue2ButtonPressed:(id)sender; 22 | -(IBAction)removeObservingAllButtonPressed:(id)sender; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /KVOBlockBindingExample/KVOBlockBindingViewController.m: -------------------------------------------------------------------------------- 1 | #import "KVOBlockBindingViewController.h" 2 | #import "ExampleModel.h" 3 | #import "NSObject+KVOBlockBinding.h" 4 | 5 | @implementation KVOBlockBindingViewController 6 | @synthesize model; 7 | @synthesize stepper1; 8 | @synthesize stepper2; 9 | @synthesize stepper1Label; 10 | @synthesize stepper2Label; 11 | 12 | - (void)dealloc 13 | { 14 | self.model = nil; 15 | [stepper1 release]; 16 | [stepper2 release]; 17 | [stepper1Label release]; 18 | [stepper2Label release]; 19 | [super dealloc]; 20 | } 21 | 22 | -(void)bindObserverToValue1 23 | { 24 | toggleObservingValue1Button.selected = YES; 25 | [toggleObservingValue1Button setTitle:@"Stop Observing Value1" forState:UIControlStateNormal]; 26 | [self.model addObserverForKeyPath:@"exampleValue1" owner:self block:^(id observed, NSDictionary *change) { 27 | label1.text = [NSString stringWithFormat:@"%d", self.model.exampleValue1]; 28 | }]; 29 | } 30 | 31 | -(void)bindObserverToValue2 32 | { 33 | toggleObservingValue2Button.selected = YES; 34 | [toggleObservingValue2Button setTitle:@"Stop Observing Value2" forState:UIControlStateNormal]; 35 | [self.model addObserverForKeyPath:@"exampleValue2" owner:self block:^(id observed, NSDictionary *change) { 36 | label2.text = [NSString stringWithFormat:@"%@ -> %@", [change valueForKey:NSKeyValueChangeOldKey] , [change valueForKey:NSKeyValueChangeNewKey]]; 37 | }]; 38 | } 39 | 40 | -(void)twoWayBinding 41 | { 42 | [self bind:stepper1 keyPath:@"value" to:stepper2 keyPath:@"value" addReverseBinding:YES]; 43 | 44 | // add one way blocks to observe the stepper values 45 | [self observe:stepper1 keyPath:@"value" block:^(id observed, NSDictionary *change) { 46 | stepper1Label.text = [NSString stringWithFormat:@"%1.0f",stepper1.value]; 47 | }]; 48 | [self observe:stepper2 keyPath:@"value" block:^(id observed, NSDictionary *change) { 49 | stepper2Label.text = [NSString stringWithFormat:@"%1.0f",stepper2.value]; 50 | }]; 51 | 52 | } 53 | 54 | -(void)removeObserverFromValue1 55 | { 56 | toggleObservingValue1Button.selected = NO; 57 | [self.model removeAllBlockBasedObserversForKeyPath:@"exampleValue1"]; 58 | [toggleObservingValue1Button setTitle:@"Start Observing Value1" forState:UIControlStateNormal]; 59 | } 60 | 61 | -(void)removeObserverFromValue2 62 | { 63 | toggleObservingValue2Button.selected = NO; 64 | [self.model removeAllBlockBasedObserversForKeyPath:@"exampleValue2"]; 65 | [toggleObservingValue2Button setTitle:@"Start Observing Value2" forState:UIControlStateNormal]; 66 | } 67 | 68 | -(void)viewDidLoad 69 | { 70 | self.model = [[[ExampleModel alloc] init] autorelease]; 71 | self.model.exampleValue1 = 0; 72 | self.model.exampleValue2 = 0; 73 | 74 | label1.text = @"0"; 75 | label2.text = @"0"; 76 | 77 | [self bindObserverToValue1]; 78 | [self bindObserverToValue2]; 79 | 80 | [self twoWayBinding]; 81 | } 82 | 83 | 84 | -(IBAction)pressMeButtonPressed:(id)sender 85 | { 86 | self.model.exampleValue1+=1; 87 | self.model.exampleValue2+=2; 88 | } 89 | 90 | -(IBAction)toggleObservingValue1ButtonPressed:(UIButton*)sender 91 | { 92 | sender.selected ? [self removeObserverFromValue1] : [self bindObserverToValue1]; 93 | } 94 | 95 | -(IBAction)toggleObservingValue2ButtonPressed:(UIButton*)sender 96 | { 97 | sender.selected ? [self removeObserverFromValue2] : [self bindObserverToValue2]; 98 | } 99 | 100 | -(IBAction)removeObservingAllButtonPressed:(id)sender 101 | { 102 | [self removeObserverFromValue1]; 103 | [self removeObserverFromValue2]; 104 | } 105 | 106 | - (void)viewDidUnload { 107 | [self setStepper1:nil]; 108 | [self setStepper2:nil]; 109 | [self setStepper1Label:nil]; 110 | [self setStepper2Label:nil]; 111 | [super viewDidUnload]; 112 | } 113 | @end -------------------------------------------------------------------------------- /KVOBlockBindingExample/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /KVOBlockBindingExample/en.lproj/KVOBlockBindingViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1280 5 | 11D50b 6 | 1938 7 | 1138.32 8 | 568.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 933 12 | 13 | 14 | YES 15 | IBUIButton 16 | IBUIStepper 17 | IBUIView 18 | IBUILabel 19 | IBProxyObject 20 | 21 | 22 | YES 23 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 24 | 25 | 26 | PluginDependencyRecalculationVersion 27 | 28 | 29 | 30 | YES 31 | 32 | IBFilesOwner 33 | IBCocoaTouchFramework 34 | 35 | 36 | IBFirstResponder 37 | IBCocoaTouchFramework 38 | 39 | 40 | 41 | 274 42 | 43 | YES 44 | 45 | 46 | 292 47 | {{258, 28}, {42, 21}} 48 | 49 | 50 | 51 | NO 52 | YES 53 | 7 54 | NO 55 | IBCocoaTouchFramework 56 | Label 57 | 58 | 1 59 | MCAwIDAAA 60 | 61 | 62 | 1 63 | 10 64 | 65 | 1 66 | 17 67 | 68 | 69 | Helvetica 70 | 17 71 | 16 72 | 73 | 74 | 75 | 76 | 292 77 | {{258, 146}, {42, 21}} 78 | 79 | 80 | 81 | NO 82 | YES 83 | 7 84 | NO 85 | IBCocoaTouchFramework 86 | Label 87 | 88 | 89 | 1 90 | 10 91 | 92 | 93 | 94 | 95 | 96 | 292 97 | {{73, 403}, {174, 37}} 98 | 99 | 100 | 101 | NO 102 | IBCocoaTouchFramework 103 | 0 104 | 0 105 | 1 106 | Update Model values 107 | 108 | 3 109 | MQA 110 | 111 | 112 | 1 113 | MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA 114 | 115 | 116 | 3 117 | MC41AA 118 | 119 | 120 | Helvetica-Bold 121 | Helvetica 122 | 2 123 | 15 124 | 125 | 126 | Helvetica-Bold 127 | 15 128 | 16 129 | 130 | 131 | 132 | 133 | 292 134 | {{43, 249}, {234, 37}} 135 | 136 | 137 | 138 | NO 139 | IBCocoaTouchFramework 140 | 0 141 | 0 142 | 1 143 | Stop Observing All KeyPaths 144 | 145 | 146 | 1 147 | MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 292 156 | {{20, 21}, {195, 37}} 157 | 158 | 159 | 160 | NO 161 | IBCocoaTouchFramework 162 | 0 163 | 0 164 | 1 165 | Stop Observing Value 1 166 | 167 | 168 | 1 169 | MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 292 178 | {{20, 139}, {195, 37}} 179 | 180 | 181 | 182 | NO 183 | IBCocoaTouchFramework 184 | 0 185 | 0 186 | 1 187 | Stop Observing Value 2 188 | 189 | 190 | 1 191 | MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 292 200 | {{95, 301}, {131, 21}} 201 | 202 | 203 | 204 | _NS:328 205 | NO 206 | YES 207 | 7 208 | NO 209 | IBCocoaTouchFramework 210 | Two-way Binding 211 | 212 | 213 | 1 214 | 10 215 | 216 | 217 | 218 | 219 | 220 | 268 221 | {{43, 330}, {94, 27}} 222 | 223 | 224 | 225 | _NS:992 226 | NO 227 | YES 228 | IBCocoaTouchFramework 229 | 0 230 | 0 231 | 5 232 | 10 233 | 234 | 235 | 236 | 268 237 | {{183, 330}, {94, 27}} 238 | 239 | 240 | 241 | _NS:992 242 | NO 243 | YES 244 | IBCocoaTouchFramework 245 | 0 246 | 0 247 | 5 248 | 10 249 | 250 | 251 | 252 | 292 253 | {{69, 365}, {42, 21}} 254 | 255 | 256 | 257 | _NS:328 258 | NO 259 | YES 260 | 7 261 | NO 262 | IBCocoaTouchFramework 263 | 5 264 | 265 | 266 | 1 267 | 10 268 | 1 269 | 270 | 271 | 272 | 273 | 274 | 292 275 | {{209, 365}, {42, 21}} 276 | 277 | 278 | 279 | _NS:328 280 | NO 281 | YES 282 | 7 283 | NO 284 | IBCocoaTouchFramework 285 | 5 286 | 287 | 288 | 1 289 | 10 290 | 1 291 | 292 | 293 | 294 | 295 | {{0, 20}, {320, 460}} 296 | 297 | 298 | 299 | 300 | 3 301 | MC43NQA 302 | 303 | 2 304 | 305 | 306 | NO 307 | 308 | IBCocoaTouchFramework 309 | 310 | 311 | 312 | 313 | YES 314 | 315 | 316 | view 317 | 318 | 319 | 320 | 7 321 | 322 | 323 | 324 | label1 325 | 326 | 327 | 328 | 14 329 | 330 | 331 | 332 | label2 333 | 334 | 335 | 336 | 15 337 | 338 | 339 | 340 | toggleObservingValue1Button 341 | 342 | 343 | 344 | 20 345 | 346 | 347 | 348 | toggleObservingValue2Button 349 | 350 | 351 | 352 | 21 353 | 354 | 355 | 356 | removeObservingAllButton 357 | 358 | 359 | 360 | 22 361 | 362 | 363 | 364 | stepper1 365 | 366 | 367 | 368 | 43 369 | 370 | 371 | 372 | stepper2 373 | 374 | 375 | 376 | 44 377 | 378 | 379 | 380 | stepper1Label 381 | 382 | 383 | 384 | 47 385 | 386 | 387 | 388 | stepper2Label 389 | 390 | 391 | 392 | 48 393 | 394 | 395 | 396 | pressMeButtonPressed: 397 | 398 | 399 | 7 400 | 401 | 13 402 | 403 | 404 | 405 | removeObservingAllButtonPressed: 406 | 407 | 408 | 7 409 | 410 | 23 411 | 412 | 413 | 414 | toggleObservingValue1ButtonPressed: 415 | 416 | 417 | 7 418 | 419 | 24 420 | 421 | 422 | 423 | toggleObservingValue2ButtonPressed: 424 | 425 | 426 | 7 427 | 428 | 25 429 | 430 | 431 | 432 | 433 | YES 434 | 435 | 0 436 | 437 | YES 438 | 439 | 440 | 441 | 442 | 443 | -1 444 | 445 | 446 | File's Owner 447 | 448 | 449 | -2 450 | 451 | 452 | 453 | 454 | 6 455 | 456 | 457 | YES 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 8 474 | 475 | 476 | 477 | 478 | 9 479 | 480 | 481 | 482 | 483 | 10 484 | 485 | 486 | 487 | 488 | 11 489 | 490 | 491 | 492 | 493 | 16 494 | 495 | 496 | 497 | 498 | 18 499 | 500 | 501 | 502 | 503 | 30 504 | 505 | 506 | 507 | 508 | 39 509 | 510 | 511 | 512 | 513 | 40 514 | 515 | 516 | 517 | 518 | 41 519 | 520 | 521 | 522 | 523 | 42 524 | 525 | 526 | 527 | 528 | 529 | 530 | YES 531 | 532 | YES 533 | -1.CustomClassName 534 | -1.IBPluginDependency 535 | -2.CustomClassName 536 | -2.IBPluginDependency 537 | 10.IBPluginDependency 538 | 11.IBPluginDependency 539 | 16.IBPluginDependency 540 | 18.IBPluginDependency 541 | 30.IBPluginDependency 542 | 39.IBPluginDependency 543 | 40.IBPluginDependency 544 | 41.IBPluginDependency 545 | 42.IBPluginDependency 546 | 6.IBPluginDependency 547 | 8.IBPluginDependency 548 | 9.IBPluginDependency 549 | 550 | 551 | YES 552 | KVOBlockBindingViewController 553 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 554 | UIResponder 555 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 556 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 557 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 558 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 559 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 560 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 561 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 562 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 563 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 564 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 565 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 566 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 567 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 568 | 569 | 570 | 571 | YES 572 | 573 | 574 | 575 | 576 | 577 | YES 578 | 579 | 580 | 581 | 582 | 48 583 | 584 | 585 | 586 | YES 587 | 588 | KVOBlockBindingViewController 589 | UIViewController 590 | 591 | YES 592 | 593 | YES 594 | pressMeButtonPressed: 595 | removeObservingAllButtonPressed: 596 | toggleObservingValue2ButtonPressed: 597 | 598 | 599 | YES 600 | id 601 | id 602 | id 603 | 604 | 605 | 606 | YES 607 | 608 | YES 609 | pressMeButtonPressed: 610 | removeObservingAllButtonPressed: 611 | toggleObservingValue2ButtonPressed: 612 | 613 | 614 | YES 615 | 616 | pressMeButtonPressed: 617 | id 618 | 619 | 620 | removeObservingAllButtonPressed: 621 | id 622 | 623 | 624 | toggleObservingValue2ButtonPressed: 625 | id 626 | 627 | 628 | 629 | 630 | YES 631 | 632 | YES 633 | label1 634 | label2 635 | removeObservingAllButton 636 | stepper1 637 | stepper1Label 638 | stepper2 639 | stepper2Label 640 | toggleObservingValue1Button 641 | toggleObservingValue2Button 642 | 643 | 644 | YES 645 | UILabel 646 | UILabel 647 | UIButton 648 | UIStepper 649 | UILabel 650 | UIStepper 651 | UILabel 652 | UIButton 653 | UIButton 654 | 655 | 656 | 657 | YES 658 | 659 | YES 660 | label1 661 | label2 662 | removeObservingAllButton 663 | stepper1 664 | stepper1Label 665 | stepper2 666 | stepper2Label 667 | toggleObservingValue1Button 668 | toggleObservingValue2Button 669 | 670 | 671 | YES 672 | 673 | label1 674 | UILabel 675 | 676 | 677 | label2 678 | UILabel 679 | 680 | 681 | removeObservingAllButton 682 | UIButton 683 | 684 | 685 | stepper1 686 | UIStepper 687 | 688 | 689 | stepper1Label 690 | UILabel 691 | 692 | 693 | stepper2 694 | UIStepper 695 | 696 | 697 | stepper2Label 698 | UILabel 699 | 700 | 701 | toggleObservingValue1Button 702 | UIButton 703 | 704 | 705 | toggleObservingValue2Button 706 | UIButton 707 | 708 | 709 | 710 | 711 | IBProjectSource 712 | ./Classes/KVOBlockBindingViewController.h 713 | 714 | 715 | 716 | 717 | 0 718 | IBCocoaTouchFramework 719 | 720 | com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 721 | 722 | 723 | YES 724 | 3 725 | 933 726 | 727 | 728 | -------------------------------------------------------------------------------- /KVOBlockBindingExample/en.lproj/MainWindow.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1024 5 | 10D571 6 | 786 7 | 1038.29 8 | 460.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 112 12 | 13 | 14 | YES 15 | 16 | 17 | 18 | YES 19 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 20 | 21 | 22 | YES 23 | 24 | YES 25 | 26 | 27 | YES 28 | 29 | 30 | 31 | YES 32 | 33 | IBFilesOwner 34 | IBCocoaTouchFramework 35 | 36 | 37 | IBFirstResponder 38 | IBCocoaTouchFramework 39 | 40 | 41 | IBCocoaTouchFramework 42 | 43 | 44 | KVOBlockBindingViewController 45 | 46 | 47 | 1 48 | 49 | IBCocoaTouchFramework 50 | NO 51 | 52 | 53 | 54 | 292 55 | {320, 480} 56 | 57 | 1 58 | MSAxIDEAA 59 | 60 | NO 61 | NO 62 | 63 | IBCocoaTouchFramework 64 | YES 65 | 66 | 67 | 68 | 69 | YES 70 | 71 | 72 | delegate 73 | 74 | 75 | 76 | 4 77 | 78 | 79 | 80 | viewController 81 | 82 | 83 | 84 | 11 85 | 86 | 87 | 88 | window 89 | 90 | 91 | 92 | 14 93 | 94 | 95 | 96 | 97 | YES 98 | 99 | 0 100 | 101 | 102 | 103 | 104 | 105 | -1 106 | 107 | 108 | File's Owner 109 | 110 | 111 | 3 112 | 113 | 114 | KVOBlockBinding App Delegate 115 | 116 | 117 | -2 118 | 119 | 120 | 121 | 122 | 10 123 | 124 | 125 | 126 | 127 | 12 128 | 129 | 130 | 131 | 132 | 133 | 134 | YES 135 | 136 | YES 137 | -1.CustomClassName 138 | -2.CustomClassName 139 | 10.CustomClassName 140 | 10.IBEditorWindowLastContentRect 141 | 10.IBPluginDependency 142 | 12.IBEditorWindowLastContentRect 143 | 12.IBPluginDependency 144 | 3.CustomClassName 145 | 3.IBPluginDependency 146 | 147 | 148 | YES 149 | UIApplication 150 | UIResponder 151 | KVOBlockBindingViewController 152 | {{234, 376}, {320, 480}} 153 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 154 | {{525, 346}, {320, 480}} 155 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 156 | KVOBlockBindingAppDelegate 157 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 158 | 159 | 160 | 161 | YES 162 | 163 | 164 | YES 165 | 166 | 167 | 168 | 169 | YES 170 | 171 | 172 | YES 173 | 174 | 175 | 176 | 15 177 | 178 | 179 | 180 | YES 181 | 182 | UIWindow 183 | UIView 184 | 185 | IBUserSource 186 | 187 | 188 | 189 | 190 | KVOBlockBindingAppDelegate 191 | NSObject 192 | 193 | YES 194 | 195 | YES 196 | viewController 197 | window 198 | 199 | 200 | YES 201 | KVOBlockBindingViewController 202 | UIWindow 203 | 204 | 205 | 206 | YES 207 | 208 | YES 209 | viewController 210 | window 211 | 212 | 213 | YES 214 | 215 | viewController 216 | KVOBlockBindingViewController 217 | 218 | 219 | window 220 | UIWindow 221 | 222 | 223 | 224 | 225 | IBProjectSource 226 | KVOBlockBindingAppDelegate.h 227 | 228 | 229 | 230 | KVOBlockBindingAppDelegate 231 | NSObject 232 | 233 | IBUserSource 234 | 235 | 236 | 237 | 238 | KVOBlockBindingViewController 239 | UIViewController 240 | 241 | IBProjectSource 242 | KVOBlockBindingViewController.h 243 | 244 | 245 | 246 | 247 | YES 248 | 249 | NSObject 250 | 251 | IBFrameworkSource 252 | Foundation.framework/Headers/NSError.h 253 | 254 | 255 | 256 | NSObject 257 | 258 | IBFrameworkSource 259 | Foundation.framework/Headers/NSFileManager.h 260 | 261 | 262 | 263 | NSObject 264 | 265 | IBFrameworkSource 266 | Foundation.framework/Headers/NSKeyValueCoding.h 267 | 268 | 269 | 270 | NSObject 271 | 272 | IBFrameworkSource 273 | Foundation.framework/Headers/NSKeyValueObserving.h 274 | 275 | 276 | 277 | NSObject 278 | 279 | IBFrameworkSource 280 | Foundation.framework/Headers/NSKeyedArchiver.h 281 | 282 | 283 | 284 | NSObject 285 | 286 | IBFrameworkSource 287 | Foundation.framework/Headers/NSObject.h 288 | 289 | 290 | 291 | NSObject 292 | 293 | IBFrameworkSource 294 | Foundation.framework/Headers/NSRunLoop.h 295 | 296 | 297 | 298 | NSObject 299 | 300 | IBFrameworkSource 301 | Foundation.framework/Headers/NSThread.h 302 | 303 | 304 | 305 | NSObject 306 | 307 | IBFrameworkSource 308 | Foundation.framework/Headers/NSURL.h 309 | 310 | 311 | 312 | NSObject 313 | 314 | IBFrameworkSource 315 | Foundation.framework/Headers/NSURLConnection.h 316 | 317 | 318 | 319 | NSObject 320 | 321 | IBFrameworkSource 322 | UIKit.framework/Headers/UIAccessibility.h 323 | 324 | 325 | 326 | NSObject 327 | 328 | IBFrameworkSource 329 | UIKit.framework/Headers/UINibLoading.h 330 | 331 | 332 | 333 | NSObject 334 | 335 | IBFrameworkSource 336 | UIKit.framework/Headers/UIResponder.h 337 | 338 | 339 | 340 | UIApplication 341 | UIResponder 342 | 343 | IBFrameworkSource 344 | UIKit.framework/Headers/UIApplication.h 345 | 346 | 347 | 348 | UIResponder 349 | NSObject 350 | 351 | 352 | 353 | UISearchBar 354 | UIView 355 | 356 | IBFrameworkSource 357 | UIKit.framework/Headers/UISearchBar.h 358 | 359 | 360 | 361 | UISearchDisplayController 362 | NSObject 363 | 364 | IBFrameworkSource 365 | UIKit.framework/Headers/UISearchDisplayController.h 366 | 367 | 368 | 369 | UIView 370 | 371 | IBFrameworkSource 372 | UIKit.framework/Headers/UITextField.h 373 | 374 | 375 | 376 | UIView 377 | UIResponder 378 | 379 | IBFrameworkSource 380 | UIKit.framework/Headers/UIView.h 381 | 382 | 383 | 384 | UIViewController 385 | 386 | IBFrameworkSource 387 | UIKit.framework/Headers/UINavigationController.h 388 | 389 | 390 | 391 | UIViewController 392 | 393 | IBFrameworkSource 394 | UIKit.framework/Headers/UIPopoverController.h 395 | 396 | 397 | 398 | UIViewController 399 | 400 | IBFrameworkSource 401 | UIKit.framework/Headers/UISplitViewController.h 402 | 403 | 404 | 405 | UIViewController 406 | 407 | IBFrameworkSource 408 | UIKit.framework/Headers/UITabBarController.h 409 | 410 | 411 | 412 | UIViewController 413 | UIResponder 414 | 415 | IBFrameworkSource 416 | UIKit.framework/Headers/UIViewController.h 417 | 418 | 419 | 420 | UIWindow 421 | UIView 422 | 423 | IBFrameworkSource 424 | UIKit.framework/Headers/UIWindow.h 425 | 426 | 427 | 428 | 429 | 0 430 | IBCocoaTouchFramework 431 | 432 | com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS 433 | 434 | 435 | 436 | com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 437 | 438 | 439 | YES 440 | KVOBlockBinding.xcodeproj 441 | 3 442 | 112 443 | 444 | 445 | -------------------------------------------------------------------------------- /KVOBlockBindingExample/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | int main(int argc, char *argv[]) 4 | { 5 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 6 | int retVal = UIApplicationMain(argc, argv, nil, nil); 7 | [pool release]; 8 | return retVal; 9 | } 10 | -------------------------------------------------------------------------------- /KVOBlockBindingTests/KVOBlockBindingTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | au.com.rayh.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /KVOBlockBindingTests/KVOBlockBindingTests-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'KVOBlockBindingTests' target in the 'KVOBlockBindingTests' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /KVOBlockBindingTests/KVOBlockBindingTests.h: -------------------------------------------------------------------------------- 1 | #import 2 | @class ExampleModel; 3 | 4 | @interface KVOBlockBindingTests : SenTestCase { 5 | BOOL wasBlockCalled; 6 | } 7 | 8 | @property (nonatomic, retain) ExampleModel *model; 9 | @property (nonatomic, assign) id binding; 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /KVOBlockBindingTests/KVOBlockBindingTests.m: -------------------------------------------------------------------------------- 1 | #import "KVOBlockBindingTests.h" 2 | #import "NSObject+KVOBlockBinding.h" 3 | #import "ExampleModel.h" 4 | 5 | @implementation KVOBlockBindingTests 6 | @synthesize model, binding; 7 | 8 | - (void)setUp 9 | { 10 | [super setUp]; 11 | self.model = [[[ExampleModel alloc] init] autorelease]; 12 | self.model.exampleValue1 = 1; 13 | self.model.exampleValue2 = -30; 14 | wasBlockCalled = NO; 15 | } 16 | 17 | - (void)tearDown 18 | { 19 | self.binding = nil; 20 | self.model = nil; 21 | [super tearDown]; 22 | } 23 | 24 | - (void)bindTo:(NSString*)keyPath 25 | { 26 | __block KVOBlockBindingTests *blockSelf = self; 27 | self.binding = [self.model addObserverForKeyPath:keyPath owner:self block:^(id observed, NSDictionary *change) { 28 | blockSelf->wasBlockCalled = YES; 29 | }]; 30 | } 31 | 32 | - (void)testShouldUnbindWhenRemoveObserverForKeyPathIsCalled { 33 | [self bindTo:@"exampleValue1"]; 34 | 35 | self.model.exampleValue1 = 2; 36 | 37 | STAssertTrue(wasBlockCalled, @"Expected the block to be called"); 38 | wasBlockCalled = NO; 39 | [self.model removeAllBlockBasedObserversForOwner:self]; 40 | 41 | self.model.exampleValue1 = 4; 42 | 43 | STAssertFalse(wasBlockCalled, @"Expected the block NOT to be called"); 44 | } 45 | 46 | - (void)testShouldUnbindWhenRemoveAllObserversIsCalled { 47 | [self bindTo:@"exampleValue1"]; 48 | 49 | self.model.exampleValue1 = 2; 50 | 51 | STAssertTrue(wasBlockCalled, @"Expected the block to be called"); 52 | wasBlockCalled = NO; 53 | 54 | [self.model removeAllBlockBasedObservers]; 55 | 56 | self.model.exampleValue1 = 4; 57 | 58 | STAssertFalse(wasBlockCalled, @"Expected the block NOT to be called"); 59 | } 60 | 61 | - (void)testShouldUnbindWhenInvalidateIsCalled { 62 | [self bindTo:@"exampleValue1"]; 63 | 64 | self.model.exampleValue1 = 2; 65 | 66 | STAssertTrue(wasBlockCalled, @"Expected the block to be called"); 67 | wasBlockCalled = NO; 68 | 69 | [self.binding invalidate]; 70 | 71 | self.model.exampleValue1 = 4; 72 | 73 | STAssertFalse(wasBlockCalled, @"Expected the block NOT to be called"); 74 | } 75 | 76 | - (void)testShouldCallBlockWhenKeyPathPropertyChanged 77 | { 78 | [self bindTo:@"exampleValue1"]; 79 | 80 | self.model.exampleValue1 = 2; 81 | 82 | STAssertTrue(wasBlockCalled, @"Expected the block to be called"); 83 | [self.binding invalidate]; 84 | } 85 | 86 | - (void)testShouldNotCallBlockWhenAnotherKeyPathPropertyChanged 87 | { 88 | [self bindTo:@"exampleValue2"]; 89 | 90 | self.model.exampleValue1 = 2; 91 | 92 | STAssertFalse(wasBlockCalled, @"Expected the block NOT to be called"); 93 | [self.binding invalidate]; 94 | } 95 | 96 | - (void) testTwoWayBinding 97 | { 98 | NSMutableArray *bindings = [self bind:self.model keyPath:@"exampleValue1" to:self.model keyPath:@"exampleValue2" addReverseBinding:YES]; 99 | 100 | self.model.exampleValue1 = 1; // no change 101 | STAssertFalse(self.model.exampleValue2 == self.model.exampleValue1, @"Identical changes should not trigger an update of the target object"); 102 | 103 | self.model.exampleValue1 = 10; 104 | STAssertTrue(self.model.exampleValue2 == self.model.exampleValue1, @"Source/Target binding did not product expected result. Expected: %d, Actual: %d",self.model.exampleValue1,self.model.exampleValue2); 105 | 106 | self.model.exampleValue2 = 20; 107 | STAssertTrue(self.model.exampleValue1 == self.model.exampleValue2, @"Target/Source binding did not product expected result. Expected: %d, Actual: %d",self.model.exampleValue2,self.model.exampleValue1); 108 | 109 | 110 | for (WSObservationBinding *binder in bindings) { 111 | [binding invalidate]; 112 | [[self allBlockBasedObservations] removeObject:binder]; 113 | } 114 | } 115 | @end 116 | -------------------------------------------------------------------------------- /KVOBlockBindingTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | **Copyright (c) 2012 - 2013 Justin Spahr-Summers** 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bind blocks to properties using KVO 2 | 3 | This repo contains a simple category, NSObject+WSObservation, and an example project. The category adds methods for observing a keypath using a block: 4 | 5 | 6 | - (WSObservationBinding*)observe:(id)object 7 | keyPath:(NSString *)keyPath 8 | options:(NSKeyValueObservingOptions)options 9 | block:(WSObservationBlock)block; 10 | 11 | - (WSObservationBinding*)observe:(id)object 12 | keyPath:(NSString *)keyPath 13 | block:(WSObservationBlock)block; 14 | 15 | 16 | The observer is the receiver of the message. The object is the object to observe. The keyPath is the standard ObjectiveC of referencing properties within an object tree (e.g. `@"some.path.to.property"`). The options are the same as you would pass for the traditional KVO methods. The second method simply calls the first with the default observing options of `NSKeyValueObservingOptionNew & NSKeyValueObservingOptionOld` 17 | 18 | To remove the bindings, there are three methods: 19 | 20 | This will remove all observations on the specified object for the receiver of the message: 21 | 22 | 23 | - (void)removeAllObservationsOn:(id)object; 24 | 25 | 26 | This will remove all observations on the receiver on a specified object for a particular keypath: 27 | 28 | 29 | - (void)removeAllObserverationsOn:(id)object keyPath:(NSString*)keyPath; 30 | 31 | 32 | And this will remove ALL block-based observations on the receiver 33 | 34 | 35 | - (void)removeAllObservations; 36 | 37 | 38 | Also, unlike the normal addObserver methods for KVO, these ones return an object that can be stored (assign or weak) by the caller. This object has method, `invalidate`, which allows the caller to selectively remove a particular binding without affecting any others on the same object. 39 | 40 | There is also an `invoke` method that will force the binding block to execute. 41 | 42 | The example project shows a typical use case of binding a model object's properties to a couple of UILabels and using both `invalidate` and `remove*` methods to control observation. 43 | 44 | ## Refactor Safe Keypaths 45 | 46 | The repo also contains code from Justin Spahr-Summers' [libextobjc](https://github.com/jspahrsummers/libextobjc) library to allow refactor safe keypaths. Using a string like `@"some.path.to.property"` could lead to issues when the property name changes and the compiler is not giving any warnings telling you that your KVO binding just broke. 47 | 48 | [self observe:self keyPath:@keypath(self.value) block:^(ViewController* observed, NSDictionary *change) { 49 | self.label.text = [NSString stringWithFormat:@"%f", observed.value]; 50 | }]; 51 | 52 | [self observe:self keyPath:@keypath(self, value) block:^(ViewController* observed, NSDictionary *change) { 53 | self.label.text = [NSString stringWithFormat:@"%f", observed.value]; 54 | }]; 55 | 56 | [self observe:self keyPath:@keypath(self, container.value) block:^(MyObject* observed, NSDictionary *change) { 57 | self.label.text = [NSString stringWithFormat:@"%f", observed.value]; 58 | }]; 59 | 60 | [self observe:self.container keyPath:@keypath(self.container, value) block:^(MyObject. observed, NSDictionary *change) { 61 | self.containerLabel.text = [NSString stringWithFormat:@"%f", observed.value]; 62 | }]; 63 | 64 | # License 65 | 66 | ## Contains code from libextobjc 67 | 68 | Released under the MIT License. See the 69 | [LICENSE](https://github.com/jspahrsummers/libextobjc/blob/master/LICENSE.md) 70 | file for more information. -------------------------------------------------------------------------------- /WSKVOBlockBinding.vendorspec: -------------------------------------------------------------------------------- 1 | # This is a generated version of the vendorspec. Please change the 2 | # values to suit your library. 3 | 4 | Vendor::Spec.new do |s| 5 | 6 | s.name "WSKVOBlockBinding" 7 | s.version "0.1" 8 | 9 | s.authors "Ray Hilton" 10 | s.email "ray@wirestorm.net" 11 | s.description "Use blocks to observe Objective-C properties using KVO" 12 | 13 | s.homepage "https://github.com/rayh/kvo-block-binding" 14 | s.source "git@github.com:rayh/kvo-block-binding.git" 15 | 16 | s.files Dir['KVOBlockBinding/**'] 17 | 18 | end 19 | --------------------------------------------------------------------------------