Должен ли я создать подкласс класса NSMutableArray - PullRequest
20 голосов
/ 05 октября 2010

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

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

Ответы [ 6 ]

32 голосов
/ 06 октября 2010

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

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

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

Если, например, вам нужен удобный метод 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"];
5 голосов
/ 05 октября 2010

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

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

4 голосов
/ 05 октября 2010

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

3 голосов
/ 30 января 2015

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

Если выподкласс NSMutableArray (или NSArray)? Зависит от того, чего вы хотите достичь.Если вы хотите добавить только метод расширения базовых функций массива, например, сортировку, тогда ответ @PayloW Categories.Однако, если вы хотите создать собственный класс, который ведет себя как массив, тогда да, подкласс NSMutableArray довольно прост.Но поскольку это кластер классов, он не совсем подкласс, как вы ожидаете.Обычно в подклассе методы, доступные в Суперклассе, доступны вашему подклассу, или вы можете переопределить их.С кластерами классов вы ДОЛЖНЫ вместо этого включать методы 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's 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's 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];

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

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

2 голосов
/ 31 мая 2013

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

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

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

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

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

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

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

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

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

0 голосов
/ 14 января 2015

Из справочника 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];
}
...