Skip to content

Instantly share code, notes, and snippets.

@vilhalmer
Created May 6, 2014 21:59
Show Gist options
  • Save vilhalmer/05df839c545e86e0c8ff to your computer and use it in GitHub Desktop.
Save vilhalmer/05df839c545e86e0c8ff to your computer and use it in GitHub Desktop.
NSObject+TryMethod
//
// NSObject+TryMethod.h
// SUPER HAX
//
// Created by Bill Doyle on 2014-05-06.
//
#import <Foundation/Foundation.h>
@interface NSObject (TryMethod)
- (id)try;
/** @return: An object that checks if its "host" object implements a method and calls it if so, or does nothing if not.
**
** This provides a nice shortcut to the verbose "if respondsToSelector:, do selector" pattern.
** For example:
** [[object try] someMethodThatMightExist:argument orNot:YES];
** Rather than:
** if ([object respondsToSelector:@selector(someMethodThatMightExist:orNot:)]) {
** [object someMethodThatMightExist:argument orNot:YES];
** }
**
** This saves a lot of space, and is fairly straightforward to parse when reading. It has the added benefit of not
** limiting the number of arguments you can use, or forcing you to mess with NSInvocation. **/
@end
//
// NSObject+TryMethod.m
// SUPER HAX
//
// Created by Bill Doyle on 2014-05-06.
//
#import "NSObject+TryMethod.h"
#pragma mark - The magic object
@interface NSObject_TryMethod_Whack : NSObject
/// The object that "takes a whack at it", if you will. ;)
- (instancetype)initWithHost:(id)aHost;
@end
@implementation NSObject_TryMethod_Whack
{
id host;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
/// The docs say that this method should call itself on super to continue the chain. However, NSObject's implementation
/// calls doesNotRecognizeSelector:, which we never want to trigger. So, we stop here.
{
if ([host respondsToSelector:[anInvocation selector]]) {
[anInvocation invokeWithTarget:host];
}
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
/// So this is a tricky bit. This method *must* return an NSMethodSignature if we don't want to throw an exception.
/// However, we're only going to have one if the host actually responds. So, if it doesn't, we have to build a fake
/// signature. Thanks to http://borkware.com/rants/agentm/elegant-delegation/ for figuring that out, though I think my
/// implementation is nicer. :)
{
if ([host respondsToSelector:aSelector]) {
return [host methodSignatureForSelector:aSelector];
}
else {
return [NSMethodSignature signatureWithObjCTypes:"@^v^c"];
}
}
- (instancetype)initWithHost:(id)aHost
{
if (!(self = [super init])) return nil;
host = aHost;
return self;
}
- (id)try
/// Overridden to prevent infinite recursion.
{
return nil;
}
@end
#pragma mark - The actual category
@implementation NSObject (TryMethod)
- (id)try
{
return [[NSObject_TryMethod_Whack alloc] initWithHost:self];
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment