Вставить объект в конец массива, используя новый литеральный синтаксис

PHP имеет:

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

Где строка будет добавлена ​​в конец массива.

Есть ли эквивалент этого в новом буквальном синтаксисе Objective-C? Самый краткий способ, которым я могу думать, делает это:

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

но может там что-то получше?

2 ответа

Решение

Посмотрите документацию по новому буквальному синтаксису. Когда вы присваиваете массиву с использованием нижнего индекса, вызов переводится в setObject:atIndexedSubscript: вызов метода:

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

Если бы у вас была собственная реализация изменяемого массива, вы могли бы добавить специальный случай к setObject:atIndexedSubscript: метод для увеличения массива при назначении за размер массива. Я очень сомневаюсь в дефолте NSMutableArray Реализация делает что-то подобное, скорее всего, вы просто получите исключение из-за отсутствия индекса. И это действительно то, что NSMutableArray см. справочную документацию:

Если индекс равен count, элемент добавляется в конец массива, увеличивая массив.

Спасибо Мартин за хедз-ап!

На самом деле я не рекомендую делать это, но Objective-C позволяет расширить setObject:atIndexedSubscript: метод с вашим собственным кодом. Этот процесс называется "метод Swizzling", и это объясняется здесь: http://darkdust.net/writings/objective-c/method-swizzling

Вот некоторый фактический рабочий код, который демонстрирует процесс. Крутой бит в main() где я могу использовать fib[-1] = ... вместо fib[fib.count] = ..., Конечно, здесь нет огромного преимущества; код не более эффективен и, конечно, труднее для чтения. Но я спасаю от необходимости выписывать fib " дважды.

Основным недостатком этого подхода является то, что Objective-C на самом деле не имеет каких-либо правил о порядке загрузки категорий, поэтому, если кто-то предоставит категорию с аналогичной функциональностью, это будет проверка, какая из них была загружена последней., (И если бы они случайно выбрали одно и то же имя для своей категории, я думаю, это был бы список, который вообще был загружен).

Итак, суть, не делайте этого, но это возможно.

#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;
}
Другие вопросы по тегам