Several new features were added to the Apple LLVM Compiler 4.0 to allow the use of Objective-C Literals and object subscripts.
This means that it is now possible to use the following shorthands to create and access elements of an NSArray
.
1 2 3 |
|
During compilation the subscripted array access is converted to a call to the objectAtIndexedSubscript:
method. So the above NSLog
statement would be converted to the following before compilation.
1 2 3 |
|
This addition to the Objective-C language is useful and certainly will make some code less verbose and easier to read.
However, many languages (particularly scripting languages) allow the use of negative indexes. Negative indexes are counted from the end of the array rather than the beginning. This led me to wonder if you can use negative indexes with the new Objective-C syntax.
Unfortunately, a quick look at the method definition for objectAtIndexedSubscript:
shows that the index parameter is an NSUInteger
, an unsigned integer. Therefore, as implemented, the new subscript indexes must be positive.
1
|
|
So, is it possible to add functionality to NSArray
to allow the use of negative indexes? The answer is Yes. The Objective-C runtime allows us to add the functionality to make it possible.
In order to do this we need to create our own version of the objectAtIndexedSubscript:
method. The new method, BLC_objectAtIndexedSubscript:
will look like:
1 2 3 4 5 6 7 |
|
You will notice that the parameter is now an NSInteger
rather than the unsigned NSUInteger
used by the original method.
You may also have noticed what appears to be a recursive call in line 6. As things stand at the moment this is a recursive call but the next step is to perform a method swizzle.
Method swizzling involves swapping two implementations of a method, in our case objectAtIndexedSubscript:
and BLC_objectAtIndexedSubscript:
. We perform the method swizzle using the method_exchangeImplementations
function of the Objective-C runtime, declared in the objc/runtime.h
header file.
The method swizzle is performed as follows:
1 2 3 4 |
|
Now that the method implementations have been swizzled that recursive call in BLC_objectAtIndexedSubscript:
has become a call to the original method. The original method implementation is now called using the BLC_objectAtIndexedSubscript:
selector.
Once we have swapped the methods we are able to use negative indexes as follows:
1 2 3 |
|
The full implementation of the new method and the method swizzling is contained in a category on NSArray
. The full listing is shown below:
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
|
As you can see the above implementation also contains a category on NSMutableArray
which swizzles a replacement method for setObject:atIndexedSubscript:
to allow negative indexes in array assignment.
There is no associated header file for this category as the category does not add any new public methods to the NSArray
(or NSMutableArray
class). Simply compiling the above .m
file into a binary will add the functionality to the array classes.
This category is available as a Gist at: https://gist.github.com/4076357
WARNING: This functionality is a hack and relies on the clang
compiler converting array subscripts into the associated call to objectAtIndexedSubscript:
without checking the positiveness of the index.