Created
September 1, 2009 22:18
-
-
Save rebo/179428 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// NSObject+AssociatedObjects.m | |
// | |
// Created by Andy Matuschak on 8/27/09. | |
// Public domain | |
// | |
// edited by rebo to add message forwarding, so extended objects can call zero-argument void | |
// blocks using [instance method] format. | |
// | |
// Not really recommended as this produces "may not respond" warnings in compiler. | |
#import "NSObject+AssociatedObjects.h" | |
#import <objc/runtime.h> | |
#import "typedefs.h" | |
@implementation NSObject (AMAssociatedObjects) | |
- (void)associateValue:(id)value withKey:(void *)key | |
{ | |
objc_setAssociatedObject(self, key, value, OBJC_ASSOCIATION_RETAIN); | |
} | |
- (id)associatedValueForKey:(void *)key | |
{ | |
return objc_getAssociatedObject(self, key); | |
} | |
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector | |
{ | |
SEL sel = @selector(extendedMethodCall:); | |
NSMethodSignature * sig ; | |
if ( aSelector != sel){ | |
sig = [[self class] instanceMethodSignatureForSelector:sel]; | |
} else { | |
sig = nil; | |
} | |
return sig; | |
} | |
- (void)forwardInvocation: (NSInvocation *) invocation | |
{ | |
SEL sel = [invocation selector]; | |
[invocation setArgument: &sel atIndex:2]; | |
[invocation setSelector: @selector(extendedMethodCall:)]; | |
return [invocation invokeWithTarget:self]; | |
} | |
- (void) extendedMethodCall:(SEL)aSelector; | |
{ | |
WorkBlk_t extendedMethod = [self associatedValueForKey:aSelector]; | |
if (extendedMethod){ | |
extendedMethod(); | |
} | |
} | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// NSObject+StateMachine.m | |
#import "NSObject+StateMachine.h" | |
#import "NSObject+AssociatedObjects.h" | |
#import <objc/runtime.h> | |
#import "typedefs.h" | |
@implementation NSObject (StateMachine) | |
- (void)stateMachineExtendInstanceMethods { | |
//Mix-in Instance Variables | |
// | |
// | |
NSMutableArray * transitions = [[NSMutableArray alloc] init]; | |
NSMutableArray * callbacks = [[NSMutableArray alloc] init]; | |
NSString * state = nil; | |
objc_setAssociatedObject(self, @selector(transitions), transitions, OBJC_ASSOCIATION_RETAIN); | |
objc_setAssociatedObject(self, @selector(callbacks), callbacks, OBJC_ASSOCIATION_RETAIN); | |
objc_setAssociatedObject(self, @selector(state), state, OBJC_ASSOCIATION_RETAIN); | |
//Mix-in Methods | |
// | |
// | |
WorkBlk_t initializeState = ^{ | |
NSLog(@"Initiailizing State ..."); | |
objc_setAssociatedObject(self, @selector(state), @"INACTIVE", OBJC_ASSOCIATION_RETAIN); | |
}; | |
objc_setAssociatedObject(self, @selector(initializeState), [initializeState copy], OBJC_ASSOCIATION_RETAIN); | |
TransBlk_t addTransition = ^(NSString * transitionName, NSString * startState, NSString * endState){ | |
NSLog(@"Adding Transition ..."); | |
SEL transitionSelector = NSSelectorFromString(transitionName); | |
[transitions addObject:[NSDictionary dictionaryWithObjectsAndKeys: transitionName,@"transitionName", startState,@"startState", endState, @"endState",nil]]; | |
WorkBlk_t callTransition = ^{ | |
NSLog(@"Applying Transition..."); | |
NSString *state = objc_getAssociatedObject(self, @selector(state)); | |
if (state == startState){ | |
objc_setAssociatedObject(self, @selector(state), endState, OBJC_ASSOCIATION_RETAIN); | |
//call backs | |
NSMutableArray * callbacks = objc_getAssociatedObject(self, @selector(callbacks)); | |
NSUInteger i = [callbacks indexOfObjectPassingTest:^BOOL(id obj, NSUInteger index, BOOL * stop){ if ([obj valueForKey:@"transitionName"]== transitionName){ return YES; }else{return NO;} }]; | |
if ( i != NSNotFound ){ | |
WorkBlk_t callback = [[callbacks objectAtIndex:i] valueForKey:@"callback"]; | |
callback(); | |
} | |
} | |
}; | |
objc_setAssociatedObject(self, transitionSelector , [callTransition copy], OBJC_ASSOCIATION_RETAIN); | |
return transitionSelector; | |
}; | |
objc_setAssociatedObject(self, @selector(addTransition), [addTransition copy], OBJC_ASSOCIATION_RETAIN); | |
CallbackBlk_t addCallbackForTransition = ^(NSString * transitionName, WorkBlk_t callback){ | |
NSLog(@"Adding CallBack ..."); | |
NSMutableArray * callbacks = objc_getAssociatedObject(self, @selector(callbacks)); | |
// [transitions addObject:[NSDictionary dictionaryWithObjectsAndKeys: transitionName,@"transitionName", startState,@"startState", endState, @"endState",nil]]; | |
[callbacks addObject:[NSDictionary dictionaryWithObjectsAndKeys:transitionName,@"transitionName", callback, @"callback",nil]]; | |
}; | |
objc_setAssociatedObject(self, @selector(addCallbackForTransition), [addCallbackForTransition copy], OBJC_ASSOCIATION_RETAIN); | |
} | |
+(void)stateMachineExtendClassMethods{ | |
// OK even more stupid stuff | |
// By adding a category class method you can add class 'methods' and 'ivars' | |
// | |
// You can even add pseudo-instance methods on arbitrary classes at run time | |
// as shown in the third objc_setAssociatedObject call below | |
NSMutableArray * transitions = [[NSMutableArray alloc] init]; | |
[transitions addObject:@"foo"]; | |
objc_setAssociatedObject(self, @selector(transitions), transitions, OBJC_ASSOCIATION_RETAIN); | |
WorkBlk_t longDescription = ^{ | |
NSLog(@"Here is a long description of the class %@", [self description]); | |
}; | |
objc_setAssociatedObject(self, @selector(longDescription), [longDescription copy], OBJC_ASSOCIATION_RETAIN); | |
InstanceBlk_t describeInstance = ^(id obj){ | |
//check obj is right kind of class | |
if ([obj isKindOfClass:self]){ | |
NSLog(@" This is an pseudo-instance method, called on %@", [obj description]); | |
} else { NSLog(@"Warning instance is not a %@", [self description]);} | |
}; | |
objc_setAssociatedObject(self, @selector(describeInstance), [describeInstance copy], OBJC_ASSOCIATION_RETAIN); | |
} | |
@end | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// StateMachineAppDelegate.m | |
// StateMachine | |
// | |
// | |
// CRAZY ATTEMPT TO SEE HOW MUCH I CAN MANGLE objc_setAssociatedObject | |
// DO NOT USE THIS CODE:P | |
// The idea is to add functionality to arbitrary objects | |
// so one can have ruby extend style behaviour through Objective-c categories | |
// It works by adding a single category method, within which instance variables and 'method' blocks | |
// are set up by making use of objc_setAssociatedObject. | |
// I wanted to see how easy it would be to use, well it is possible but it is really unwieldy | |
// and makes code very hard to read. | |
// see NSObject+StateMachine.m for the core of the work, it is a dumb partially implemented state machine | |
// that in theory can be used for any instance of any NSObject. | |
#import "StateMachineAppDelegate.h" | |
#import "NSObject+StateMachine.h" | |
#import "NSObject+AssociatedObjects.h" | |
#import "typedefs.h" | |
@implementation StateMachineAppDelegate | |
@synthesize window; | |
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { | |
// Insert code here to initialize your application | |
// StateMachine * sm = [[StateMachine alloc] init]; | |
NSDictionary * dictStateMachine = [[NSDictionary alloc] init]; | |
[dictStateMachine stateMachineExtendInstanceMethods]; | |
// Initialize State | |
WorkBlk_t initializeState = [dictStateMachine associatedValueForKey:@selector(initializeState)]; | |
initializeState(); | |
//Check State | |
NSLog(@"Object State is :%@", [dictStateMachine associatedValueForKey:@selector(state)]); | |
//Add a transition from Inactive to Active | |
TransBlk_t selectorByAddingTransition = [dictStateMachine associatedValueForKey:@selector(addTransition)]; | |
SEL activate = selectorByAddingTransition( @"ACTIVATE", @"INACTIVE", @"ACTIVE"); | |
// Activate | |
WorkBlk_t activateState = [dictStateMachine associatedValueForKey:activate]; | |
activateState(); | |
//Check Final State | |
NSLog(@"Object State is :%@", [dictStateMachine associatedValueForKey:@selector(state)]); | |
// Reset and Run Again | |
NSLog(@"\n\nReseting and Running Again:"); | |
initializeState(); | |
activateState(); | |
NSLog(@"\n\nAdding A Callback to Activate State:"); | |
// How about a callback when activating | |
CallbackBlk_t addCallbackToTransition = [dictStateMachine associatedValueForKey:@selector(addCallbackForTransition)]; | |
addCallbackToTransition(@"ACTIVATE", ^{ NSLog(@"Warning State Has Been Activated!") ;}); | |
// Reset and Run Again This time With Call Back In Effect | |
NSLog(@"\n\nReseting and Running Again, after callback has been added:"); | |
initializeState(); | |
activateState(); | |
NSLog(@"Object State is :%@", [dictStateMachine associatedValueForKey:@selector(state)]); | |
NSLog(@"Object With additional methods and state is a : %@", [dictStateMachine className]); | |
NSLog(@"\n\n\n Creating New Object"); | |
NSString * sm = [[NSString alloc] init]; | |
[sm stateMachineExtendInstanceMethods]; | |
// Initialize State | |
WorkBlk_t initializeState_b = [sm associatedValueForKey:@selector(initializeState)]; | |
initializeState_b(); | |
//Check State | |
NSLog(@"Object State is :%@", [sm associatedValueForKey:@selector(state)]); | |
//Add a transition from Inactive to Active | |
TransBlk_t selectorByAddingTransition_b = [sm associatedValueForKey:@selector(addTransition)]; | |
SEL activate_b = selectorByAddingTransition_b( @"PAINT", @"INACTIVE", @"PAINTED"); | |
// Activate | |
WorkBlk_t activateState_b = [sm associatedValueForKey:activate_b]; | |
activateState_b(); | |
//Check Final State | |
NSLog(@"Object State is :%@", [sm associatedValueForKey:@selector(state)]); | |
// Reset and Run Again | |
NSLog(@"\n\nReseting and Running Again:"); | |
initializeState_b(); | |
activateState_b(); | |
NSLog(@"Object State is :%@", [sm associatedValueForKey:@selector(state)]); | |
NSLog(@"\n\nAdding A Callback to Activate State:"); | |
// How about a callback when activating | |
CallbackBlk_t addCallbackToTransition_b = [sm associatedValueForKey:@selector(addCallbackForTransition)]; | |
addCallbackToTransition_b(@"PAINT", ^{ NSLog(@"Hey This Object Has Been Painted!") ;}); | |
// Reset and Run Again This time With Call Back In Effect | |
NSLog(@"\n\nReseting and Running Again, after callback has been added:"); | |
initializeState_b(); | |
activateState_b(); | |
NSLog(@"Object With additional methods and state is a : %@", [sm className]); | |
//added.... | |
// Can add class instance variables, class methods, and also pseudo-instance methods on arbitrary classes: | |
[NSArray stateMachineExtendClassMethods]; | |
NSArray * array = [NSArray associatedValueForKey:@selector(transitions)]; | |
NSLog( @"Class instance array = %@" , array); | |
WorkBlk_t longDescription = [NSArray associatedValueForKey:@selector(longDescription)]; | |
longDescription(); | |
NSArray * arr = [NSArray arrayWithObjects:@"bar",nil]; | |
InstanceBlk_t describeInstance = [NSArray associatedValueForKey:@selector(describeInstance)]; | |
describeInstance(arr); | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment