2010-10-05 197 views
18

我有一个NSMutableArray对象,我想添加自定义方法。我尝试了继承NSMutableArray,但是当我尝试使用count方法获取对象的数量时,出现错误,指出“方法仅为抽象类定义”。为什么count方法不被继承?我应该继承NSMutableArray类

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

回答

32

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

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

为了便于比较,下面是我在implementing a custom NSMutableArray subclass回写的一个例子。

+1

我想评论一下,实现一个自定义类型安全的NSMutableArray子类是浪费时间。编写这个自定义子类的时间花在编写一些单元测试来验证你的* actual *应用程序逻辑是合理的,而不是增加更多混乱。您的额外代码仅仅是更多的代码来维护,为bug和安全缺陷添加更多攻击向量,而不会为最终用户带来任何实际益处。 – PeyloW 2010-10-06 14:28:49

+1

@PeyloW:一个很好的理论位置,但最终会出现真实世界的代码,其中类型安全是有用的。正如在我写的文章中所描述的那样,您将任何失败都推向了注入的地步,而不是稍后的任意失误。通过消除恶意对象替换的可能性,减少了攻击面。 – 2010-10-06 14:54:03

+1

@PeyloW:还有需要连接到CoreFoundation的真实世界的代码。这关心你的可变数组实现是否实际上是一个NSMutableArray子类或别的东西;在这一点你的实现有更好的子类NSMutableArray。 – 2010-10-06 15:00:30

4

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

+0

现在链接不好。有没有新的工程? – JohnK 2013-07-20 01:35:25

+0

我更新了链接。 – 2013-07-26 15:16:45

2

我个人有T o同意节点忍者和佩罗洛夫,因为从技术上讲他们都有权利。其实,这并没有多大帮助。

序言: 有在代码许多阵列,所有到一个仅含有一种,但是不同类型的数据例如classA,classB,classC。

问题: 我可以很容易地混合数组,通过错误的传递给例如一些选择器,因为它们都是NSMutableArray。没有静态检查,只有运行时检查。

解决方案 - 第一尝试:NSMutableArray里的 制作子类,所以编译器可以进行静态检查,并警告有关错误的数据类型。

因为编译器会发出警告,即使你传递错误类型-addObject或-objectAtIndex当你重载的人是好的。 这很糟糕,因为你不能通过这种方式实例化NSMutableArray超类。

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

这是一件好事,因为当你通过错误类型-addObject或-objectAtIndex当你重载那些你可以实例NSMutableClass和编译器检查。

的,坏的一面是,你需要您使用,不仅那些在课堂上的区别在于数组包含NSMutableArray中的每一个选择超载。

结论: 当您构建一些在数组中有许多类类型的复杂代码时,请相信我值得尝试。简单地通过这个编译器向我展示了几个我将不会识别的错误,直到我将在运行时面对它。甚至更糟的是,最终用户会面对它。

-1

从NSArray的苹果参考,在方法重写部分:

NSArray中的任意子类必须覆盖原始的实例方法计数和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]; 
} 
1

这是旧的文章,但认为我想补充我的经验。 @PayloW的回答是一个很好的答案,我想回答你的问题很好,但是,没有人真正回答你的问题倒过来,所以我会在这里这样做。

你应该继承NSMutableArray(或NSArray)吗?取决于你想要达到的目标。如果你只想添加一个方法来扩展数组的BASIC功能,比如排序,那么@PayloW的回答Categories就是这样。然而,如果你想创建一个类似数组的自定义类,那么是的,子类化NSMutableArray很容易。但是因为它是一个类集群,它并不像你期望的那样完全子类化。通常在子类中,超级类中可用的方法可用于您的子类,或者您可以覆盖它们。对于Class Clusters,您必须包含Super将要使用的方法,并提供一个超类的实例来包装这些方法。

下面是你如何继承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]; 

这一切工作非常好,是干净的,实际上非常酷实施和使用。

希望它有帮助。