patterncMinor
Cleaning up repeated calls to NSMakeRange()
Viewed 0 times
callsnsmakerangecleaningrepeated
Problem
In Objective-C, many of the NSString or NSMutableString methods for comparing or manipulating strings require a range argument--that is, an argument of data type
There does exist a convenience function for creating an NSRange struct:
But the problem for me happens when I'm manipulating a string. I'll sometimes write a method in which I will make several different manipulations on the string within the same method. And the range I need to cover will be the entire length of the string. It's not acceptable to simply declare an
For example, suppose we're working with strings representing file paths. We want to manipulate the string in a few ways to make sure the path looks how we want it to look:
As you can see, we have three calls to
NSRange (a struct containing a starting position and a length). This applies outside of NSString (NSArray for example), but NSString is where I see it most frequently.There does exist a convenience function for creating an NSRange struct:
NSRange range = NSMakeRange(position, length);But the problem for me happens when I'm manipulating a string. I'll sometimes write a method in which I will make several different manipulations on the string within the same method. And the range I need to cover will be the entire length of the string. It's not acceptable to simply declare an
NSRange variable, set it, and use this throughout, because if the length of the string changes, the previously set range will be incorrect.For example, suppose we're working with strings representing file paths. We want to manipulate the string in a few ways to make sure the path looks how we want it to look:
- (NSString *)cleanPath:(NSString *)originalPath inRoot:(NSString *)rootPath {
NSString *returnPath = [originalPath stringByReplacingOccurrencesOfString:@" " withString:@"" options:NSCaseInsensitiveCompare:NSMakeRange(0,[originalPath length])];
while ([returnPath rangeOfString:@"./"].location != NSNotFound) {
returnPath = [returnPath stringByReplacingOccurrencesOfString:@"./" withString:@"/" options:NSCaseInsensitiveCompare:NSMakeRange(0,[returnPath length])];
}
if ([returnPath rangeOfString:rootPath options:NSCaseInsensitiveCompare range:NSMakeRange(0,[returnPath length])] != NSNotFound) {
return returnPath;
} else {
return rootPath;
}
}As you can see, we have three calls to
NSMakeRange just to make a range that encompasses the length of the string we're working on. We want to look at the whole range. Giving a range too short will mean we're noSolution
I see a couple of ways to clean this up.
First of all, you mention not wanting to use
For example:
Now, this macro definition exists only within this method. You don't necessarily save a whole lot of characters, but autocomplete will be a lot nicer, and you don't have to explicitly tell the macro to start at 0 and go to the end...you've predefined that in the macro.
You can also just write your own C-Style function. That's all
And while defining a function is fine and dandy, now it can be seen by at least everything in the file. This is Objective-C, so we can use blocks. We can create a block within the method, and now the block, much like the
Here we've define a block called
There a couple of important differences between a regular C-style function and a block though. Scope and memory.
With this block example, the block's scope is limited only to this method. It also exists in memory on the stack and only until this method returns (or until the end of an
First of all, you mention not wanting to use
#define, but let's not forget about #undef. You could define a macro as the first line of this method and undefine it as the last line of the method.For example:
- (NSString *)cleanPath:(NSString *)originalPath inRoot:(NSString *)rootPath {
#define _STRING_RANGE(string) (NSMakeRange(0,[string length]))
// code
#undef _STRING_RANGE
}Now, this macro definition exists only within this method. You don't necessarily save a whole lot of characters, but autocomplete will be a lot nicer, and you don't have to explicitly tell the macro to start at 0 and go to the end...you've predefined that in the macro.
You can also just write your own C-Style function. That's all
NSMakeRange is after all.NSRange stringRange(NSString *string) {
return NSMakeRange(0,[string length]);
}And while defining a function is fine and dandy, now it can be seen by at least everything in the file. This is Objective-C, so we can use blocks. We can create a block within the method, and now the block, much like the
#define macro that we #undef exists only within the method:- (NSString *)cleanPath:(NSString *)originalPath inRoot:(NSString *)rootPath {
NSRange (^stringRange)(NSString *) = (^NSString *string) {
return NSMakeRange(0,[string length]);
}
// code...
}Here we've define a block called
stringRange, that returns a NSRange and takes a single argument of type NSString *. This is exactly the same as the function we wrote in option two, and it's even called in the exact same way too.stringRange(myString)There a couple of important differences between a regular C-style function and a block though. Scope and memory.
With this block example, the block's scope is limited only to this method. It also exists in memory on the stack and only until this method returns (or until the end of an
@autoreleasepool it was created in, whichever comes first).Code Snippets
- (NSString *)cleanPath:(NSString *)originalPath inRoot:(NSString *)rootPath {
#define _STRING_RANGE(string) (NSMakeRange(0,[string length]))
// code
#undef _STRING_RANGE
}NSRange stringRange(NSString *string) {
return NSMakeRange(0,[string length]);
}- (NSString *)cleanPath:(NSString *)originalPath inRoot:(NSString *)rootPath {
NSRange (^stringRange)(NSString *) = (^NSString *string) {
return NSMakeRange(0,[string length]);
}
// code...
}stringRange(myString)Context
StackExchange Code Review Q#47404, answer score: 4
Revisions (0)
No revisions yet.