Skip to content

Instantly share code, notes, and snippets.

@mbixby
Last active December 14, 2015 17:59
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 mbixby/5125879 to your computer and use it in GitHub Desktop.
Save mbixby/5125879 to your computer and use it in GitHub Desktop.
EXTNull
/**
EXTNull
Experimental
EXTNull is a subclass of NSNull that acts more like nil.
Calling unrecognized methods on EXTNull *will not* throw
a NSInvalidArgumentException but again return EXTNull.
You can redefine `EXTNull` to `Null` if you like clean-looking
code (just insert #define EXTNULL_SHORTHAND in your prefix file).
@example
[Null dasherized] ~> Null
@example
[@[@"A", Null, @"C"] map:method(lowercased)] ~> @[@"a", Null, @"c"];
(`lowercased` returns a copy of self with lowercased strings)
(`method()` makes a block out of a method – an equivalent of & operator in ruby)
@example
[@[@"a", Null, @"c"] compacted] ~> @[@"a", @"c"]
(compacted returns a copy of self with NSNull values removed)
Beware of passing EXTNull as an argument; the method would
need to support this. See [NSObject ext_pure] for how to avoid this.
@example
id string = Null;
[@"life" appendString:string] ~> Exception, since Null is not a NSString
[self gooutwith:string] ~> Exception, since Null is not a Person
@see RACTuple from ReactiveCocoa
@see the Maybe monad (http://bit.ly/data-maybe) as a possible replacement
@see license and updates at https://gist.github.com/mbixby/5125879
*/
#define EXTNull ((id)[_EXTNull null])
/**
Changes nil to EXTNull
@example
id unknown = nil;
@[maybe(unknown), maybe(@"")] ~> @[Null, @""];
@example
id one, id two = nil, @"two"
[@[maybe(one), maybe(two)] joinedBy:@" "] ~> @"two" (instead of @" two")
(joinedBy is implemented as [[array compacted] componentsJoinedByString:...])
(compacted returns a copy of self with NSNull values removed)
@see [NSObject pure], the reverse of maybe()
*/
#define ext_maybe(OBJECT) ((id)(OBJECT ?: EXTNull))
@interface _EXTNull : NSObject
+ (instancetype)null;
@end
@interface NSNull (EXTNullAdditions)
- (instancetype)ext_pure;
@end
@interface NSObject (EXTNullAdditions)
/**
Returns YES if self isn't nil or NSNull or EXTNull
@example
id value = Null;
!!value ~> YES
[value isNotNull] ~> NO
*/
- (BOOL)ext_isNotNull;
/**
Returns the underlying value of the object.
For most objects, it returns self.
EXTNull overrides this method to return nil.
@example
Null.pure ~> nil
@"".pure ~> @""
*/
- (instancetype)ext_pure;
@end
#ifdef EXTNULL_SHORTHAND
#define Null EXTNull
#define maybe ext_maybe
@interface NSObject (EXTNullShorthandAdditions)
- (BOOL)isNotNull;
- (instancetype)pure;
@end
#endif
#import "EXTNull.h"
/**
@see license and updates at https://gist.github.com/mbixby/5125879
*/
@implementation _EXTNull
+ (instancetype)null
{
static dispatch_once_t once;
static id null;
dispatch_once(&once, ^{
null = [self new];
});
return null;
}
// Overrides 'ext_pure' from NSObject+EXTNullAdditions
- (instancetype)ext_pure
{
return nil;
}
- (BOOL)respondsToSelector:(SEL)sel
{
return YES;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
return [super methodSignatureForSelector:sel]
?: [super methodSignatureForSelector:@selector(ext_pure)];
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
id null = self;
[invocation setReturnValue:&null];
}
- (instancetype)returnNull
{
return self;
}
- (BOOL)isKindOfClass:(Class)aClass
{
return [aClass isSubclassOfClass:NSNull.class]
|| [super isKindOfClass:aClass];
}
- (BOOL)isMemberOfClass:(Class)aClass
{
return [aClass isSubclassOfClass:NSNull.class]
|| [super isMemberOfClass:aClass];
}
@end
@implementation NSNull (EXTNullAdditions)
// Overrides 'ext_pure' from NSObject+EXTNullAdditions
- (instancetype)ext_pure
{
return nil;
}
@end
@implementation NSObject (EXTNullAdditions)
- (BOOL)ext_isNotNull
{
return self.ext_pure;
}
- (instancetype)ext_pure
{
return self;
}
@end
#ifdef EXTNULL_SHORTHAND
@implementation NSObject (EXTNullShorthandAdditions)
- (BOOL)isNotNull
{
return self.ext_isNotNull;
}
- (instancetype)pure
{
return self.ext_pure;
}
@end
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment