我有一个NSMutableArray对象,我想添加自定义方法。我尝试了子类化NSMutableArray但是当我尝试使用count方法获取对象数时,我得到一个错误,说“只为抽象类定义了方法”。为什么count方法没有被继承?
我在其他地方读到如果我想使用它们,我必须将一些NSMutableArray方法导入我的自定义类。我只想为NSMutableArray类添加一个自定义方法。我应该将NSMutableArray子类化,还是应该做其他事情?
答案 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];
}