在Objective-C中子类化NSArray

5
我需要一个类,它拥有NSArray的所有方法,但是两个方法被修改了。
我想在我的自定义类中重写这两个方法:
1)countByEnumeratingWithState:objects:count:
2)objectAtIndex:
经过几个小时的研究,我发现没有合理的方法可以做到这一点,因为:
- 我不想使用分类,因为并非所有的NSArray实例都应该具有修改后的行为。(而且会引发警告) - 我不想重新编写所有的初始化器、arrayWith...方法、基本方法以及实现自己的存储(因为这些功能已经在Cocoa中实现了,对吧?为什么要重新实现一个已经存在的类的所有功能呢?) - 如果我让我的自定义类继承NSObject,并在ivar中使用NSArray作为存储,那么当在Xcode中编程时,所有NSArray的方法都将不可用(即使我可以将它们转发到NSArray ivar中) - 我曾经尝试使用method_setImplementation(...)按需覆盖方法实现,但仍然无法找到一种方式,在运行时动态创建一个类,然后该类将具有我提到的2个方法的自定义实现。
期待您的想法!谢谢

2
不是重复的问题。另一个问题并没有真正回答提问者的问题。 - bbum
我认为我会创建一个包装类。应该能够使用resolveInstanceMethod、forwardingTargetForSelector等方法盲目地转发大多数调用。 - Hot Licks
2
回答OP的问题:您只需要覆盖主要方法以创建子类。这些方法在主@interface中声明。基类中的所有其他方法都是基于这些方法实现的。除此之外,根据自定义行为和/或优化,覆盖所需内容即可。代理包装器是可能的,但与之相比真的非常慢。 - bbum
@bbum -- 你必须仔细阅读NSArray的详细信息 -- "要重写的方法"包括一个相当令人生畏的函数列表,具体取决于你如何阅读它。 - Hot Licks
2
一篇有用的文章 http://www.cocoawithlove.com/2008/12/ordereddictionary-subclassing-cocoa.html - vikingosegundo
显示剩余4条评论
2个回答

9
口诀:如果某些操作很困难(或似乎需要比必要的更多代码),则很可能您的设计违反了iOS / OS X框架的设计原则。重新审视您的设计可能会得出更好的解决方案。
回答最初的问题,如果想要子类化NSArray(或NSMutableArray),则需要实现基本方法,没有更多也没有更少。
基本方法是在类本身的@interface中声明的方法。例如:
@interface NSArray : NSObject
- (NSUInteger)count;
- (id)objectAtIndex:(NSUInteger)index;
@end

对于NSMutableArray:

@interface NSMutableArray : NSArray
- (void)addObject:(id)anObject;
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index;
- (void)removeLastObject;
- (void)removeObjectAtIndex:(NSUInteger)index;
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject;
@end

如果您子类化NSMutableArray并实现上述7个方法(也包括NSArray的两个方法),则会得到一个NSMutableArray子类,假设您的方法正确实现,则与消耗可变数组的所有API兼容。
这是因为类簇的设计方式。公共类是抽象的,从不直接实例化。它们提供了一个原始接口,其中包含类的核心功能,然后是所有其他非原语API(除了初始化器,见下文)的具体实现,这些实现是基于原语实现的。具体的私有子类然后覆盖所有原语和一些非原语,以为特定配置提供最佳行为。

我想要一个NSArray实例用于我正在开发的库,并且我希望它透明地适用于我的库的用户。也就是说。 对于他们来说,在使用普通NSArray和 我将提供的修改类之间没有任何区别。也就是说。这是一个存储问题, 终端用户不应该关心,界面 应该与NSArray保持相同 - 因此在那时失去所有init方法 不是真正的选择。

初始化方法不是NSArray的原语接口的一部分。您正在添加超出“使类与NSArray / NSMutableArray兼容”所定义的文档的要求。这没有什么错,只是指出。
之所以如此,是因为很少有子类化集合类以提供您所描述的业务逻辑。集合在其行为上非常通用,而将条件化集合行为的此类业务逻辑将在管理整个模型层对象图的类中完成。
如果您确实想要这样做,请提供您想要的任何init *方法的实现,并根据需要调用包装的通用实例。初始化器的实现并没有什么特别之处,您不会在这样做中损失太多。
也无需实现所有内容。实现一个或两个,并在其余部分上@throw一个描述性异常。
如果您决定转发接受var-args的那些方法,则无法直接使用它们,因为没有va_list接受方法。相反,您将希望将参数的va_list转换为语言数组(即id[] foo = malloc(... * sizeof(id));),并将其传递给initWithObjects:count:
一些其他评论:
  • 你正在做的事情 [在子类中提供完整的NS*Array接口] 看起来很困难,因为这不是一个常见的模式,框架设计师认为没有必要创建一个支持它的设计。几乎总是最好在对象图中更高级别的位置实现原始集合级别的自定义行为。几乎总是。

  • method_setImplementation()和动态类创建在学术上很有趣,但几乎从来不是解决方案。显然,搞乱NSArray或NSMutableArray类(或具体实现类)会炸掉依赖标准行为的其他框架。除此之外,它是一种动态OO组合的模式,不是真正意图在Objective-C中使用;维护将是一件麻烦的事情。


讲解得非常清楚,点赞。你也可以提及重新实现原始方法(类族)的技术原因。 - user529758
我必须说,在经过几天的考虑后,我真的同意你在这里提出的观点。通过多年与苹果的经验,我本应该知道更好 - 对于他们来说只有一种正确的方法。然而,你在Cocoa方面的看法是非常准确的,如果它在Cocoa中非常难做到,那么你就走错了路。我选择子类化,实现原始方法,实现一个新的init方法并覆盖我想要修改的方法。这不是我想要的100%,但已经足够接近了。 - Marin Todorov

0

为什么不基于NSObject创建一个包含NSArray的新类,而不是子类化NSArray呢?

这样,您就可以使用NSArray的所有函数,并添加自己的方法来执行自定义操作。

或者您确实需要一个NSArray吗?


我想为我正在开发的库创建一个NSArray实例,并且我希望它对我的库的用户透明工作。也就是说,对于他们来说,使用普通的NSArray和我提供的修改后的类之间没有区别。也就是说,这是一个存储问题,终端用户不应该关心,接口应该与NSArray保持相同 - 因此在那一点上失去所有init方法并不是真正的选择。 - Marin Todorov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接