Push object onto end of array using new literal syntax

1.8k Views Asked by At

PHP has:

arr[] = 'Push this onto my array';

Where the string will be added to the end of the array.

Is there any equivalent of this in the new Objective-C literal syntax? The most succinct way I can think of doing it is:

arr[arr.count] = @"Add me";

but maybe there's something better out there?

2

There are 2 best solutions below

1
On BEST ANSWER

Take a look at the documentation for the new literal syntax. When you assign into an array using a subscript, the call is translated into the setObject:atIndexedSubscript: method call:

NSMutableArray *foo = …;
foo[0] = @"bar"; // => [foo setObject:@"bar" atIndexedSubscript:0];

If you had your own mutable array implementation, you could add a special case to the setObject:atIndexedSubscript: method to grow the array when assigning past the array size. I very much doubt the default NSMutableArray implementation does anything like it, most probably you’d just get an exception about index being out of bounds. And this is indeed what NSMutableArray does, see the reference documentation:

If the index is equal to count the element is added to the end of the array, growing the array.

Thank you Martin for the heads-up!

0
On

I don't actually recommend doing this, but Objective-C does make it possible to extend the setObject:atIndexedSubscript: method with your own code. The process is called "method swizzling", and it's explained here: http://darkdust.net/writings/objective-c/method-swizzling

Here's some actual working code that demonstrates the process. The cool bit is in main(), where I'm able to use fib[-1] = ... instead of fib[fib.count] = .... Of course there's no huge advantage here; the code is no more efficient, and certainly harder to read. But I do save having to write out "fib" twice.

A major disadvantage of this approach is that Objective-C doesn't really have any rules about the order in which categories get loaded, so if somebody else provided a category with similar functionality, it would be a toss-up which one got loaded last. (And if they happened to choose the same name for their category, it would be a toss-up which one got loaded at all, I think.)

So, bottom line, don't do this: but it is possible.

#import <Foundation/Foundation.h>
#import <objc/runtime.h>

@interface NSMutableArray (NegativeOne)
+(void)load;
-(void)swizzled_setObject:(id)obj atIndexedSubscript:(NSUInteger)idx;
@end

@implementation NSMutableArray (NegativeOne)

+(void)load
{
    Method original = class_getInstanceMethod(self, @selector(setObject:atIndexedSubscript:));
    Method swizzled = class_getInstanceMethod(self, @selector(swizzled_setObject:atIndexedSubscript:));
    method_exchangeImplementations(original, swizzled);
}

-(void)swizzled_setObject:(id)obj atIndexedSubscript:(NSUInteger)idx
{
    if (idx == -1)  idx = [self count];
    [self swizzled_setObject:obj atIndexedSubscript:idx];  // go use the old method: not a typo!
}

@end

int main()
{
    int x = 0, y = 1;
    NSMutableArray *fib = [NSMutableArray new];

    for (int i=0; i < 10; ++i) {
        fib[-1] = @(x);  // wowie zowie!
        int temp = x+y;  x = y;  y = temp;
    }
    NSLog(@"%@", fib);
    return 0;
}