I'm trying to create a method which checks for a null/nil/empty string, and I'm trying to get it working as a category but having no luck.
I'm using this code, based on answers in this topic:
@implementation NSString (NSStringExtension)
- (BOOL)isNullOrEmpty {
return self == nil ||
self == (id)[NSNull null] ||
[@"" isEqualToString:self] ||
[[self stringByReplacingOccurrencesOfString:@" " withString:@""] length] == 0||
[self isEqualToString:@"(null)"]
|| ([self respondsToSelector:@selector(length)] && [(NSData *) self length] == 0)
|| ([self respondsToSelector:@selector(count)] && [(NSArray *) self count] == 0)
|| [[self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] == 0;
}
@end
Yet when I try to use this this is what I get:
NSLog([@"" isNullOrEmpty] ? @"1":@"0"); // prints 1
NSString *s1 = nil;
NSLog([s1 isNullOrEmpty] ? @"1":@"0"); // prints 0
NSLog([args.itemName isNullOrEmpty] ? @"1":@"0"); // prints 0
NSLog([(NSString*)nil isNullOrEmpty] ? @"1":@"0"); // prints 0
This is baffling me, and I can only assume that some combination of iOS5/ARC is causing the nil object to be coerced to a blank string/pointer. The debugger shows the string as 0x0, yet when I use my isNullOrEmpty
method, I get false.
return self == nil
This can never happen. If you try to send isNullOrEmpty
(or any other message) to nil
, nothing happens (objc_msgSend()
, the function responsible for message dispatch, checks for a nil
reciever as one of the first things it does and aborts).
self == (id)[NSNull null]
This will also never happen. If you send isNullOrEmpty
to an object that's an instance of NSNull
, your method here, which is a method on NSString
, will not be called. Instead, NSNull
's version (which probably doesn't exist) will be.
Likewise, ([self respondsToSelector:@selector(count)] && [(NSArray *) self count])
is never going to happen. If the object is an NSArray
, then isNullOrEmpty
will never run, because, again, it's a method of NSString
.
Correspondingly, [(NSData *) self length]
doesn't do what you think it does. NSString
instances do respond to length
, but casting the object to NSData
doesn't use the NSData
version of the method -- it still ends up as the NSString
version of length
, because the object actually is an NSString
(casting only happens at compile-time; it can't change anything at run-time).
[self isEqualToString:@"(null)"]
Here you appear to be checking for nil
again, but you are being misled by the representation that NSLog
chooses when it prints nil
:
NSLog(@"%@", nil);
This displays (null)
in the console, but that doesn't mean that the object itself is a string with those characters. NSLog
just chooses that string to display for nil
.*
Several of the things you are doing would require this to be in a category on NSObject
, so that the method would in fact be called even if the object was not an NSString
.
To check for a string consisting only of whitespace, all you need is the comparison to the empty string @""
after trimming whitespace:
NSString * trimmedSelf = [self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
// Then either:
[trimmedSelf isEqualToString:@""];
// Or:
([trimmedSelf length] == 0);
*And even better, doing NSLog(@"%@", [NSNull null]);
displays <null>
(angle brackets instead of parentheses), wonderfully confusing the first few times you encounter NSNull
.