I have repeatedly had trouble with the rageOf… NSString methods, because they return a struct. Going forward I will do more to avoid them, here are some ways I plan to do it.
Sending a message that returns a struct to nil can “return” undefined values. With small structs like NSRange, you are more likely to get {0} on Intel, compared to PowerPC and iPhone/ARM. Unfortunately, this makes nil-messaging bugs hard to detect. In my experience you will miss them when running on the simulator, even if they are 100% reproducible on an actual iPhone.
This category method has helped me avoid using -rangeOfString: dangerously,
@implementation NSString (HasSubstring)
- (BOOL) hasSubstring:(NSString*)substring;
{
if(IsEmpty(substring))
return NO;
NSRange substringRange = [self rangeOfString:substring];
return substringRange.location != NSNotFound && substringRange.length > 0;
}
@end
I choose to define [aString hasSubstring:@""] as NO. You might prefer to throw an exception, or differentiate between @"" and nil. But I don’t think a nil string is enough error to throw an exception. And even though technically any string contains the empty string, I generally treat @"" as semantically equivalent to nil.
As I’ve said before,
A few simple guidelines can help you avoid my misfortune:
- Be especially careful using of any objective-C method that returns a
double, struct,orlong long- Don’t write methods that return a
double,struct, orlong long. Return an object instead of astruct; anNSNumber*orfloatinstead of adoubleorlong long. If you must return a dangerous data type, then see if you can avoid it. There really isn’t a good reason to return astruct, except for efficiency. And when micro-optimizations like that matter, it makes more sense to write that procedure in straight-C, which avoids the overhead of Objective-C message-passing, and solves the undefined-return-value problem.- But if you absolutely must return a dangerous data type, then return it in a parameter. That way you can give it a default value of your choice, and won’t have undefined values if an object is unexpectedly
nil.
Bad:
- (struct CStructure) evaluateThings;
Good:
- (void) resultOfEvaluatingThings:(struct CStructure*)result;.
It’s not a bad idea to wrap up all the rangeOf methods in functions or categories that play safer with nil.
Thanks to Greg Parker for corrections!