One of the fundamental operators in functional languages is ‘cons’. It concatenates lists
of things together, and since strings are usually treated as lists of characters, cons gets
a lot of use in string processing as well. One of the cool tricks I saw done with
Objective-C a while ago was implementing cons on NSString, using : like Haskell and ML
do. I have been unable to find the original site, so I decided to reimplement it
myself recently.
A quick caveat to the following: this uses Objective-C trickery that may not be very safe
in a production environment. Also, the upcoming Automatic Reference Counting in Clang/LLVM 3.0
will not compile this code. With that out of the way, lets look at how we’re going to do this.
In Objective-C, methods are backed by a
concrete implementation in the form of a standard C function. The runtime uses the
IMP
type for this, which is defined as
1
| |
The ... in this case stands for additional arguments for the method.
For example, [@"foo" stringByAppendingString:@"bar"] will eventually be executed by a
function matching the prototype below.
1
| |
Additional arguments are just tacked onto the end. This can be combined with standard C
varargs. The only caveat is we need to signal the end of the varargs somehow. I used nil.
1 2 3 4 5 6 7 8 9 10 11 12 | |
There are two approaches to adding a method to a class at runtime in Objective-C. The
normal way is to use
class_addMethod. This is enough in most cases, but because we have a variable length selector, we can’t use this method. Another
way methods can be added is with the dynamic method resolution available through NSObject
(Mike Ash touches on this method in a post on message forwarding).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | |
Here all were doing is checking to see if the selector is completely made up of ‘:’ characters.
If it is, we add the stringCons IMP from above for that selector. Note the else block at
the end. The call to [super resolveInstanceMethod:sel] is very important, as other parts of
a class may depend on this dynamic method resolution.
The final hurdle is how to add +resolveInstanceMethod: to NSString. The safest way would be
to subclass NSString, but since NSString is a class cluster this is not possible. I ended up
using a category, but a safer choice might be to use method swizzling to insert our method
in addition to any NSString may have defined.
That’s it to adding a cons-like operator to NSString in Objective-C. To use it, just insert
it between instances of NSString ending with nil, like below.
1 2 3 4 | |