Skip to content

Instantly share code, notes, and snippets.

@beccadax
Created November 7, 2012 08:18
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save beccadax/4030150 to your computer and use it in GitHub Desktop.
Save beccadax/4030150 to your computer and use it in GitHub Desktop.
Category to make calling optional delegate methods simpler
#import <Foundation/Foundation.h>
@protocol ADPerformOptionalMethod <NSObject>
@optional
- (instancetype)performOptionalMethod;
- (instancetype)performOptionalMethodOrReturnObject:(id)returnValue;
- (instancetype)performOptionalMethodOrReturnValue:(NSValue*)returnValue;
@end
@interface NSObject (performOptionalMethod) <ADPerformOptionalMethod> @end
#import "NSObject+performOptionalMethod.h"
#import <objc/runtime.h>
typedef enum {
ADPerformOptionalMethodReturnsNothing,
ADPerformOptionalMethodReturnsObject,
ADPerformOptionalMethodReturnsValue
} ADPerformOptionalMethodReturnType;
@interface ADPerformOptionalMethodProxy : NSProxy
+ (id)proxyWithTarget:(NSObject *)target defaultReturn:(id)defaultReturn type:(ADPerformOptionalMethodReturnType)returnType;
@end
@implementation NSObject (optionalMethod)
- (id)performOptionalMethod {
return [ADPerformOptionalMethodProxy proxyWithTarget:self defaultReturn:nil type:ADPerformOptionalMethodReturnsNothing];
}
- (id)performOptionalMethodOrReturnObject:(id)returnValue {
return [ADPerformOptionalMethodProxy proxyWithTarget:self defaultReturn:returnValue type:ADPerformOptionalMethodReturnsObject];
}
- (id)performOptionalMethodOrReturnValue:(NSValue *)returnValue {
return [ADPerformOptionalMethodProxy proxyWithTarget:self defaultReturn:returnValue type:ADPerformOptionalMethodReturnsValue];
}
@end
@interface ADPerformOptionalMethodProxy ()
@property (nonatomic,strong) NSObject * target;
@property (nonatomic,assign) ADPerformOptionalMethodReturnType returnType;
@property (nonatomic,strong) id defaultReturn;
@end
@implementation ADPerformOptionalMethodProxy
+ (id)proxyWithTarget:(NSObject *)target defaultReturn:(id)defaultReturn type:(ADPerformOptionalMethodReturnType)returnType {
ADPerformOptionalMethodProxy * proxy = [ADPerformOptionalMethodProxy alloc];
proxy.target = target;
proxy.defaultReturn = defaultReturn;
proxy.returnType = returnType;
return proxy;
}
- (void)forwardInvocation:(NSInvocation *)invocation {
if([self.target respondsToSelector:invocation.selector]) {
[invocation invokeWithTarget:self.target];
return;
}
[invocation invokeWithTarget:nil];
void * returnValueBuffer = alloca(invocation.methodSignature.methodReturnLength);
switch (self.returnType) {
case ADPerformOptionalMethodReturnsNothing:
return;
case ADPerformOptionalMethodReturnsObject:
*((__autoreleasing id*)returnValueBuffer) = self.defaultReturn;
break;
case ADPerformOptionalMethodReturnsValue:
[self.defaultReturn getValue:returnValueBuffer];
break;
}
[invocation setReturnValue:returnValueBuffer];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
NSMethodSignature * sig = [NSObject instanceMethodSignatureForSelector:sel];
if(!sig) {
unsigned int numberOfProtocols;
Protocol * __unsafe_unretained * protocols = class_copyProtocolList(self.target.class, &numberOfProtocols);
for(unsigned int i = 0; i < numberOfProtocols; i++) {
struct objc_method_description desc = protocol_getMethodDescription(protocols[i], sel, NO, YES);
if(desc.name) {
sig = [NSMethodSignature signatureWithObjCTypes:desc.types];
break;
}
}
free(protocols);
}
return sig;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment