Checking if a Cocoa object is empty is a little harder then in other languages, say C++, (but easier in some ways). Because every object in Objective-C is actually a pointer to an object, there are two ways, obj
, can be empty.
obj = {}
obj
points to an object that is empty. Say an array with 0 items, or the string ""
, etc..
obj = nil
obj
, the pointer obj
, is NULL
, nil
, 0
, or whatever you want to call it. You might argue that obj
isn’t really an object, but it is empty, because there’s nothing in it.
Bug:
When I first started writing Objective-C, I made the mistake of writing code like: if([name isEqualToString:@""]){ ... }
, to test for empty strings. And this code would work for a while until I used it in a situation where name
was nil
, and then, because sending any method called on nil
“returns” NO
, I would have mysterious errors. (Worse then a crash, because it’s harder to track down.)
Bug:
It’s tempting to avoid the previous bug, by explicitly testing for nil
and {}
. Say with code like:
if (email == nil || ![email isEqualTo:@""] ) email = @"An email address is required";
But generally this is a bad idea. It means more code, which means more places for a bug. I know it’s only one trivial test, but I’m serious, when I say it’s asking for a bug — like the bug in the example above, which sets email
to @"An email address is required"
, whenever it is not the empty string, rather then when it is empty. (Values have been changed tho protect the innocent but it’s a bug I’ve seen.)
Solutions:
Wil Shipley suggests using the global function:
static inline BOOL IsEmpty(id thing) {
return thing == nil
|| ([thing respondsToSelector:@selector(length)]
&& [(NSData *)thing length] == 0)
|| ([thing respondsToSelector:@selector(count)]
&& [(NSArray *)thing count] == 0);
}
I’ve been using his IsEmpty()
for about a year. I’ve had zero problems with it, while it’s made my code more readable and concise.
Another solution is to take advantage of what happens when you send a message to nil
. (To over-simplify, you get back 0 or NO
.) So you can just say “if ([obj count] == 0)
then obj
is empty.” This often means reversing your thinking, and testing “IsNotEmpty()” instead of “IsEmpty()”. I don’t think it’s as clear is IsEmpty()
in general, but in cases where it is, there you have it.