Как расширить NSArray?

Вот моя попытка:

H-файл:

@interface Strings : NSArray
@end

М-файл:

@implementation Strings
- (id) init
{
    [self initWithObjects:
     @"One.",
     nil];
    return self;
}
@end

Когда я бегу, я получаю это:

'NSInvalidArgumentException', причина: '* -[NSArray initWithObjects:count:]: метод определен только для абстрактного класса. Определите -[Strings initWithObjects:count:]!'

Вот что я сделал вместо этого:

H-файл:

@interface Strings : NSObject
+ (NSArray*) getStrings;
@end

М-файл:

@implementation Strings
+ (NSArray*) getStrings
{
    NSArray* strings = [[NSArray alloc] initWithObjects:
     @"One.",
     nil];
    return strings;
}
@end

person Roger C S Wernersson    schedule 22.01.2012    source источник
comment
Я думал, что могу расширить NSArray, но он падает даже при компиляции. В итоге я пишу статическую функцию, подобную вашей, внутри базового класса всех моих моделей. А поскольку это статическая функция, я могу использовать [[self alloc] init] для создания элементов и добавления их в массив.   -  person John Pang    schedule 22.09.2018


Ответы (5)


NSArray – это кластер классов (ссылка на документацию Apple). Это означает, что когда вы пытаетесь создать NSArray, система создает некий частный подкласс NSArray. Класс NSArray просто определяет интерфейс; подклассы NSArray предоставляют реализации интерфейса.

Вы можете написать свой собственный подкласс NSArray, но вы должны предоставить собственное хранилище для объектов в массиве. Вы должны инициализировать это хранилище самостоятельно. Сообщение об ошибке говорит вам об этом, говоря, что вам нужно переопределить initWithObjects:count: в вашем подклассе. Ваше переопределение должно поместить объекты в любое хранилище, которое вы выделяете как часть реализации вашего класса.

Реализация NSArray вариативного метода initWithObjects: — это всего лишь оболочка вокруг initWithObjects:count:, поэтому вам не нужно реализовывать initWithObjects:.

person rob mayoff    schedule 22.01.2012
comment
Кроме того, убедитесь, что вы прочитали и поняли раздел "Примечания о подклассах" в Справочник по классу NSArray, прежде чем писать собственный подкласс NSArray. - person rob mayoff; 23.01.2012

Вы должны избегать производного от NSArray. Из документации:

Помните, что NSArray — это общедоступный интерфейс для кластера классов, и что это влечет за собой для вашего подкласса. Примитивные методы NSArray не включают назначенных инициализаторов. Это означает, что вы должны предоставить хранилище для своего подкласса и реализовать примитивные методы, которые непосредственно воздействуют на это хранилище.

Это означает, что когда вы инициализируете массив, вы не получаете экземпляр NSArray. Вы получите экземпляр совершенно другого класса, который просто имеет тот же интерфейс. Вот почему подклассы не работают так, как вы думаете: вам придется полностью реализовать хранилище самостоятельно. Вот почему в документации далее говорится:

Любой подкласс NSArray должен переопределить методы count и objectAtIndex примитивного экземпляра:. Эти методы должны работать с резервным хранилищем, которое вы предоставляете для элементов коллекции. Для этого резервного хранилища вы можете использовать статический массив, стандартный объект NSArray или какой-либо другой тип данных или механизм. Вы также можете частично или полностью переопределить любой другой метод NSArray, для которого вы хотите предоставить альтернативную реализацию.

И последнее, но не менее важное: у вас все равно была бы неправильная инициализация. Вам нужно было бы позвонить super:

- (id)init
{
    self = [super initWithObjects:@"One", @"Two", nil];
    if (!self) return nil;

    return self;
}

Но, как я только что сказал, это просто не работает так просто. Вы снова получите такое же исключение. Поэтому вам следует просто избегать производных от NSArray.

Что вы можете сделать, так это добавить категорию для добавления методов ко всем NSArray экземплярам.

person DarkDust    schedule 22.01.2012

NSArray не поддерживает создание подклассов таким образом. Однако вы можете добавить категорию, хотя это и не рекомендуется повсеместно.

Дополнительные сведения см. в разделе Objective C — Создание подклассов NSArray.

person StilesCrisis    schedule 22.01.2012
comment
Я изменил на self = [self initWithObjects: но это не помогло. - person Roger C S Wernersson; 23.01.2012
comment
Я предполагаю, что может быть так, что [NSArray alloc] на самом деле не выделяет NSArray, поэтому, когда я это делаю, реализации нет. Помощь! Но почему речь идет об initWithObjects:count: ? - person Roger C S Wernersson; 23.01.2012
comment
Как вы думаете, почему вам все равно нужно создавать подклассы NSArray? Если вы не делаете специализированный резервный магазин, это, как правило, не очень хороший подход. - person StilesCrisis; 23.01.2012

возможно

self = [super initWithObjects:...];
person Michael Krelin - hacker    schedule 22.01.2012
comment
Это правда, но для подкласса NSArray также действуют особые правила. - person StilesCrisis; 23.01.2012
comment
Истинный. Ну, я все же оставлю ответ. Перво-наперво. - person Michael Krelin - hacker; 23.01.2012

Вам нужно назначить себя и вызвать метод инициализации вашего суперкласса.

if (self = [super initWithObjects:...]) {
   ...
}

return self;
person Graham Perks    schedule 22.01.2012
comment
Это правда, но для подкласса NSArray также действуют особые правила. - person StilesCrisis; 23.01.2012