Должен ли я подклассифицировать класс NSMutableArray

У меня есть объект NSMutableArray, к которому я хочу добавить специальные методы. Я пробовал подклассифицировать NSMutableArray, но затем я получаю сообщение об ошибке "Метод, определенный только для абстрактного класса" при попытке получить количество объектов с помощью метода count. Почему метод count не унаследован?

Я где-то читал, что мне придется импортировать некоторые методы NSMutableArray в мой пользовательский класс, если я хочу их использовать. Я просто хочу добавить пользовательский метод в класс NSMutableArray. Так должен ли я подкласс NSMutableArray, или я должен сделать что-то еще?

Ответ 1

NSMutableArray не является конкретным классом, а просто абстрактным суперклассом кластера классов. В документации для NSMutableArray есть информация о том, как подкласс, но также настоятельно советует вам не делать этого! Только подкласс, если у вас есть особая потребность в фактическом хранении.

Кластер классов означает, что фактический класс будет выбран во время выполнения. Массив, созданный пустым, может не использовать тот же класс, что и массив, созданный с 1000 элементами. Время выполнения может сделать разумный выбор того, какую реализацию использовать для вас. На практике NSMutableArray будет мостом CFArray. Вам не о чем беспокоиться, но вы можете увидеть это, если вы проверите тип массивов в отладчике, вы никогда не увидите NSArray, но довольно часто NSCFArray.

Как уже упоминалось ранее, подклассификация не совпадает с расширением класса. Objective-C имеет понятие категорий. Категория похожа на то, что другие языки программирования называют mix-ins.

Если вы, например, хотите использовать метод удобства для NSMutableArray для сортировки всех членов в свойстве, тогда определите интерфейс категории в файле .h как таковой:

@interface NSMutableArray (CWFirstnameSort)
-(void)sortObjectsByProperty:(NSString*)propertyName;
@end

И реализация будет следующей:

@implementation NSMutableArray (CWFirstnameSort)
-(void)sortObjectsByProperty:(NSString*)propertyName;
{
    NSSortDescriptor* sortDesc = [NSSortDescriptor sortDescriptorWithKey:propertName ascending:YES];
    [self sortUsingDescriptors:[NSArray arrayWithObject:sortDesc]];
}
@end

Затем используйте его просто как:

[people sortObjectsByProperty:@"firstName"];

Ответ 2

Если вы просто добавляете пользовательский метод, используйте категорию в NSMutableArray. Это кластер классов, поэтому реализация обеспечивается недокументированными подклассами. Вам нужно предоставить несколько методов для генерации вашего собственного подкласса. Однако, если вы просто добавите категорию, то ваш пользовательский метод будет работать со всеми NSMutableArrays в вашем приложении.

Для сравнения, вот пример, который я написал некоторое время назад для реализации пользовательского подкласса NSMutableArray.

Ответ 3

Objective-C имеет механизм добавления методов к существующим классам, называемым Категории. Таким образом, вам не нужно создавать свой собственный подкласс.

Ответ 4

Это старый пост, но я подумал, что добавлю свой опыт. Ответ @PayloW - хороший ответ, и я думаю, что на ваш вопрос отлично отвечает, однако никто не ответил на ваш вопрос другим способом, поэтому я сделаю это здесь.

Должен ли быть подкласс NSMutableArray (или NSArray)? Зависит от того, чего вы хотите достичь. Если вы хотите добавить метод расширения функциональности BASIC, например сортировки, тогда @PayloW answer Categories. Однако, если вы хотите создать собственный класс, который ведет себя как массив, то да, подклассификация NSMutableArray довольно проста. Но поскольку это кластер классов, он не совсем подклас, как вы ожидали. Обычно в подклассе доступные в Super Class методы доступны для вашего подкласса, или вы можете их переопределить. С кластерными кластерами вы ДОЛЖНЫ включать в себя методы Super, которые вы собираетесь использовать, и предоставить экземпляр _backend суперкласса, чтобы обернуть эти методы.

Ниже приведен пример того, как вы должны использовать подкласс NSMutableArray (или любой кластер классов):

Интерфейс:

@interface MyCustomArrayClass : NSMutableArray {

    // Backend instance your class will be using
    NSMutableArray *_backendArray;
}

// *** YOUR CUSTOM METHODS HERE (no need to put the Super methods here) ***

-(bool)isEmpty;
-(id)nameAtIndex:(int)index;
-(int)rowAtIndex:(int)index;
-(int)columnAtIndex:(int)index;

@end

Реализация:

@implementation MyCustomArrayClass

-(instancetype)init {

    if (self = [super init]) {            
        _backendArray = [@[] mutableCopy];
    }

    return self;
}

// *** Super Required Methods (because you're going to use them) ***

-(void)addObject:(id)anObject {
    [_backendArray addObject:anObject];
}

-(void)insertObject:(id)anObject atIndex:(NSUInteger)index {
    [_backendArray insertObject:anObject atIndex:index];
}

-(void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject {
    [_backendArray replaceObjectAtIndex:index withObject:anObject];
}

-(id)objectAtIndex:(NSUInteger)index {
    return [_backendArray objectAtIndex:index];
}

-(NSUInteger)count {
    return _backendArray.count;
}

-(void)removeObject:(id)anObject {
    [_backendArray removeObject:anObject];
}

-(void)removeLastObject {
    [_backendArray removeLastObject];
}

-(void)removeAllObjects {
    [_backendArray removeAllObjects];
}

-(void)removeObjectAtIndex:(NSUInteger)index {
    [_backendArray removeObjectAtIndex:index];
}

// *** YOUR CUSTOM METHODS ***

-(bool)isEmpty {
    return _backendArray.count == 0;
}

-(id)nameAtIndex:(int)index {
    return ((MyObject *)_backendArray[index]).name;
}

-(int)rowAtIndex:(int)index {
    return ((MyObject *)_backendArray[index]).row;
}

-(int)columnAtIndex:(int)index {
    return ((MyObject *)_backendArray[index]).column;
}

@end

Тогда для использования так:

MyCustomArrayClass *customArray = [[MyCustomArrayClass alloc] init];

// Your custom method
int row = [customArray rowAtIndex:10];

// NSMutableArray method
[customArray removeLastObject];

// Your custom class used just like an array !!!
index = 20;
MyObject *obj = customArray[index];

Все работает очень красиво, чисто и на самом деле довольно круто реализовать и использовать.

Надеюсь, что это поможет.

Ответ 5

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

Преамбула: В коде есть много массивов, которые все к одному содержат только один, но другой тип данных, например. classA, classB, classC.

Проблема: Я могу легко смешивать массивы, передавая неправильные данные, например, какой-то селектор, потому что все они NSMutableArray. Нет статической проверки, только время выполнения.

Решение - первая попытка: Создайте подкласс NSMutableArray, поэтому компилятор делает статическую проверку и предупреждает о неправильном типе данных.

Это хорошо, потому что компилятор предупреждает вас, даже когда вы передаете неправильный тип в -addObject или -objectAtIndex при перегрузке этих. Это плохо, потому что вы не можете создать суперкласс класса NSMutableArray таким образом.

Решение - вторая попытка: Создайте новый (прокси) класс какого-либо типа, например. NSObject как для NSMutableArray и добавить член класса типа NSMutableArray.

Это хорошо, потому что вы можете создавать экземпляры NSMutableClass и компилятора, когда вы передаете неправильный тип в -addObject или -objectAtIndex при перегрузке этих.

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

Заключение: Когда вы создаете какой-то сложный код, который имеет много типов классов в своих массивах, поверьте мне, стоит попробовать. Просто, выполнив этот компилятор, я показал несколько ошибок, которые я бы не узнал, пока не столкнусь с ним во время выполнения. Или еще хуже, когда конечный пользователь столкнется с этим.

Ответ 6

Из ссылки Apple для NSArray в разделе "Методы переопределения":

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

В качестве стороннего примечания в Objective-C нет фактической функции, которая позволяет объявить класс как абстрактный класс, как таковой, например, в Java. Таким образом, то, что они делают вместо этого, вызывает что-то вроде кода ниже, из какого-то метода, который они хотят заставить переопределить подклассом. По сути, они дают семантику класса "абстрактный класс".

Это определение метода действует как абстрактный метод, который вызывает исключение, если не переопределяется, со следующим выходом:

-someAbstractFooMethod только для абстрактного класса. Определить - [YourClassName someAbstractFooMethod]!

- (void) someAbstractFooMethod
{
    //Force subclassers to override this method
    NSString *methodName = NSStringFromSelector(_cmd);
    NSString *className = [self className];
    [NSException raise:NSInvalidArgumentException
                format:@"-%@ only defined for abstract class. Define -[%@ %@]!", methodName, className, methodName];
}