Last active
December 14, 2015 17:59
-
-
Save mbixby/5125879 to your computer and use it in GitHub Desktop.
EXTNull
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
/** | |
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 |
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
#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