我应该继承NSMutableArray类

时间:2010-10-05 10:05:32

标签: objective-c cocoa-touch methods nsmutablearray subclass

我有一个NSMutableArray对象,我想添加自定义方法。我尝试了子类化NSMutableArray但是当我尝试使用count方法获取对象数时,我得到一个错误,说“只为抽象类定义了方法”。为什么count方法没有被继承?

我在其他地方读到如果我想使用它们,我必须将一些NSMutableArray方法导入我的自定义类。我只想为NSMutableArray类添加一个自定义方法。我应该将NSMutableArray子类化,还是应该做其他事情?

6 个答案:

答案 0 :(得分:32)

NSMutableArray不是具体的类,它只是类集群的抽象超类。 NSMutableArray的文档确实有关于如何子类化的信息,但强烈建议您不要这样做!如果您特别需要实际存储,则只能进行子类化。

类集群意味着将在运行时选择实际的类。创建为空的数组可能不会使用与使用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"];

答案 1 :(得分:5)

如果您只是添加自定义方法,请使用NSMutableArray上的类别。它是一个类集群,因此实现由未记录的子类提供。您需要提供一些方法来生成自己的子类。但是,如果您只是添加一个类别,那么您的自定义方法将适用于您应用中的所有 NSMutableArrays

为了比较,这是我在implementing a custom NSMutableArray subclass后面写的一个例子。

答案 2 :(得分:4)

Objective-C有一种机制,可以将方法添加到名为Categories的现有类中。这样您就不必创建自己的子类。

答案 3 :(得分:3)

这是一篇很老的帖子,但我想加上我的经验。 @PayloW的答案是一个很好的答案,我认为完美地回答了你的问题,然而,没有人真正回答你的问题,所以我会在这里做。

你应该继承NSMutableArray(或NSArray)吗?取决于你想要实现的目标。如果您只想添加一个方法来扩展数组的BASIC功能,比如排序,那么@PayloW的回答Categories就是这样。但是,如果要创建一个行为类似于数组的自定义类,那么是,子类化NSMutableArray非常简单。但是因为它是一个类集群,它并不像你期望的那样完全是子类。通常在子类中可以使用超类中可用的方法,也可以覆盖它们。对于Class Clusters,您必须包含您将要使用的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];

这一切都非常好用,很干净,实际上非常酷,可以实现和使用。

希望它有所帮助。

答案 4 :(得分:2)

我必须同意节点忍者和PeyloW,因为从技术上讲,他们都是正确的。实际上,这对我没有多大帮助。

<强>序言: 代码中有许多数组,所有数据都只包含一个但不同类型的数据,例如classA,classB,classC。

<强>问题: 我可以通过将错误的一个传递到例如一些选择器,因为它们都是NSMutableArray。没有静态检查,只有运行时一个。

解决方案 - 第一次尝试: 创建NSMutableArray的子​​类,以便编译器进行静态检查并警告错误的数据类型。

这很好,因为编译器会在您将错误的类型传递给-addObject或-objectAtIndex时向您发出警告。 这很糟糕,因为你不能用这种方式实例化NSMutableArray超类。

解决方案 - 第二次尝试: 制作某种类型的新(代理)类,例如NSObject与NSMutableArray一样,并添加类型为NSMutableArray的类成员。

这很好,因为当你将错误的类型传递给-addObject或-objectAtIndex时,你可以实例化NSMutableClass和编译器检查。

不好的一面是你需要重载你使用的NSMutableArray的每个选择器,而不仅仅是那些在数组所包含的类中不同的选择器。

<强>结论: 当你构建一些在其数组中有许多类类型的复杂代码时,相信我值得尝试。简单地说,这个编译器向我展示了几个我在运行时面对的错误。或者更糟糕的是,当最终用户面对它时。

答案 5 :(得分:-1)

来自NSArray的Apple参考,在覆盖方法部分:

NSArray的任何子类都必须覆盖原始实例方法count和objectAtIndex:。这些方法必须在您为集合元素提供的后备存储上运行。对于此后备存储,您可以使用静态数组,标准NSArray对象或某些其他数据类型或机制。您也可以选择部分或全部覆盖要为其提供替代实现的任何其他NSArray方法。

作为旁注,在Objective-C中,没有实际的功能允许您将类本身声明为抽象类,例如在Java中。所以,他们所做的就是调用类似下面代码的东西,从他们想要强制被子类覆盖的某些方法中调用。实际上,他们给了班级的抽象课程&#39;语义。

此方法定义充当抽象方法,如果未覆盖则会引发异常,并使用以下输出:

-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];
}