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 struct
s 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*
orfloat
instead of adouble
orlong 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!