Created
February 9, 2015 09:28
-
-
Save n-b/8f8e4543f394465465af to your computer and use it in GitHub Desktop.
fp-obj-c
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
//#!/usr/bin/env objc-run | |
#import <Foundation/Foundation.h> | |
#import <objc/runtime.h> | |
#import <objc/message.h> | |
/// | |
/// Typedefs | |
typedef id (^TargetMethod)(id arg); | |
typedef id (^InstanceProperty)(id target); | |
typedef InstanceProperty (^InstanceMethod)(id arg); | |
#define DefineMethod(method, argType, returnType)\ | |
- (returnType (^)(argType)) method;\ | |
+ (returnType (^(^)(id target))(argType)) method; | |
#define DefineProperty(method, returnType)\ | |
+ (returnType (^)(id target)) method; | |
/// | |
/// Dispatch | |
@implementation NSObject (FuncsDefinitions) | |
+ (BOOL) pfunc_resolveInstanceMethod:(SEL)sel | |
{ | |
NSString * pfuncName = NSStringFromSelector(sel); | |
// The methods we’re adding have no arguments in the real Objective-C sense. | |
if(![pfuncName hasSuffix:@":"]) { | |
SEL methodSelector = NSSelectorFromString([pfuncName stringByAppendingString:@":"]); | |
NSMethodSignature * signature = [self instanceMethodSignatureForSelector:methodSelector]; | |
// We’re looking for an existing method of the same name, with 1 argument. | |
// (We’d need to check the argument and return types.) | |
if(signature) { | |
// We’ve found our original method. Let’s add ours: | |
// First add a method that returns a TargetMethod block. | |
IMP imp_i = imp_implementationWithBlock(^(id target){ | |
return (TargetMethod)^(id arg) { | |
return ((id(*)(id,SEL,id))objc_msgSend)(target, methodSelector, arg); | |
}; | |
}); | |
class_addMethod(self, sel, imp_i, "@?@:"); | |
// And add a class method (that returns an InstanceMethod block, that returns an InstanceProperty) | |
IMP imp_c = imp_implementationWithBlock(^(id targetClass){ | |
return (InstanceMethod)^(id arg) { | |
return (InstanceProperty)^(id target) { | |
NSAssert(self==targetClass, @"Adding a class method: the receiver should be the class."); | |
return ((id(*)(id,SEL,id))objc_msgSend)(target, methodSelector, arg); | |
}; | |
}; | |
}); | |
// `object_getClass` returns our metaclass. Our class methods are our metaclass’s instance methods. | |
class_addMethod(object_getClass(self), sel, imp_c, "@?@:"); | |
return YES; | |
} | |
} | |
// Call the original implementation | |
return [self pfunc_resolveInstanceMethod:sel]; | |
} | |
+ (BOOL) pfunc_resolveClassMethod:(SEL)sel | |
{ | |
NSString * pfuncName = NSStringFromSelector(sel); | |
// The methods we’re adding have no arguments in the real Objective-C sense. | |
if(![pfuncName hasSuffix:@":"]) { | |
NSMethodSignature * signature = [self instanceMethodSignatureForSelector:sel]; | |
// We’re looking for an existing method of the same name, with 0 argument. | |
// (We’d need to check the return type.) | |
if(signature) { | |
// Add a method that returns a InstanceProperty block. | |
IMP imp_i = imp_implementationWithBlock(^(id targetClass){ | |
return (InstanceProperty)^(id target) { | |
return ((id(*)(id,SEL))objc_msgSend)(target, sel); | |
}; | |
}); | |
class_addMethod(object_getClass(self), sel, imp_i, "@?@:"); | |
return YES; | |
} | |
} | |
// Call the original implementation | |
return [self pfunc_resolveInstanceMethod:sel]; | |
} | |
+ (void)load { | |
method_exchangeImplementations(class_getClassMethod(self, @selector(pfunc_resolveInstanceMethod:)), | |
class_getClassMethod(self, @selector(resolveInstanceMethod:))); | |
method_exchangeImplementations(class_getClassMethod(self, @selector(pfunc_resolveClassMethod:)), | |
class_getClassMethod(self, @selector(resolveClassMethod:))); | |
} | |
@end | |
/// | |
/// Test code | |
void Print(id<NSObject> obj) { | |
printf("%s\n",obj.description.UTF8String); | |
} | |
@implementation NSArray (Map_) | |
- (NSArray*) map:(id (^)(id target))block_ | |
{ | |
NSMutableArray * result = [NSMutableArray new]; | |
for (id target in self) { | |
[result addObject:block_(target)]; | |
} | |
return result; | |
} | |
- (NSArray*) reduce:(id (^(^)(id arg))(id target))block_ | |
{ | |
id result = [self firstObject]; | |
if([self count]<2) { | |
return result; | |
} | |
for (id target in [self subarrayWithRange:NSMakeRange(1, [self count]-1)]) { | |
result = block_(target)(result); | |
} | |
return result; | |
} | |
@end | |
@implementation NSString (Plus) | |
- (NSString*) plus:(NSString*) string | |
{ | |
return [self stringByAppendingString:string]; | |
} | |
@end | |
/// | |
/// | |
@interface NSArray (Funcs) | |
DefineMethod(map, id (^)(id), NSArray*); | |
DefineMethod(reduce, id (^(^)(id))(id), NSArray*); | |
@end | |
@interface NSString (Funcs) | |
DefineProperty(uppercaseString, NSString*); | |
DefineMethod(plus, NSString*, NSString*); | |
@end | |
/// | |
/// | |
int main() | |
{ | |
Print(@[@"foo", @"bar", @"baz"].map(NSString.uppercaseString)); | |
// -> ( FOO, BAR, BAZ ) | |
Print(@"Hello".plus(@" I love you")); | |
// -> Hello I love you | |
Print(@[@"foo", @"bar", @"baz"].reduce(NSString.plus)); | |
// -> foobarbaz | |
InstanceProperty massUppercaser = NSArray.map(NSString.uppercaseString); | |
Print(massUppercaser(@[@"abc",@"def"])); | |
// -> ( ABC, DEF ) | |
InstanceProperty exclamate = NSString.plus(@"!!"); | |
Print(@[@"quick", @" to the batmobile"].map(NSString.uppercaseString).map(exclamate).reduce(NSString.plus)); | |
// -> QUICK!! TO THE BATMOBILE!! | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment