30

我在 C# 中真正喜欢的是通用列表。只能包含一种类型的对象的列表。Cocoa/Objective-C 中是否有类似通用列表的东西?到目前为止,我只知道NSArray谁将指向任何对象。

4

4 回答 4

34

在 Cocoa 应用程序中想要这个通常是设计薄弱的标志。

NSArray是不可变的,因此它不会“获取指向任何对象的指针”,并且在交给您时可能已经包含正确的对象。我假设您更担心的是NSMutableArray您认为代码的其他部分可能会添加错误类型的对象。但是看看 Cocoa 本身;将可变数组作为类设计的一部分公开是非常罕见的。

相反,您通常会公开一个NSArray和几个用于修改该数组的方法。类似于以下内容:

@class Foo : NSObject
- (NSArray *)bars;
- (void)addBar:(Bar *)bar;
- (void)removeBar:(Bar *)bar;
@end

这通常会通过发出编译器警告来阻止插入错误的对象,然后当然您可以在其中添加断言-addBar:-removeBar:如果您愿意的话。

于 2009-04-27T15:27:58.573 回答
9

Objective-C 不支持泛型编程。您总是可以使用 Objective-C++ 和 STL 列表。

于 2009-04-27T14:09:00.450 回答
3

通用 NSArrays 可以通过子类化来实现NSArray,并用更严格的方法重新定义所有提供的方法。例如,

- (id)objectAtIndex:(NSUInteger)index

将不得不重新定义

@interface NSStringArray : NSArray

作为

- (NSString *)objectAtIndex:(NSUInteger)index

一个 NSArray 只包含 NSStrings。

创建的子类可以用作替代品,并带来许多有用的功能:编译器警告、属性访问、更好的代码创建和 Xcode 中的补全。所有这些都是编译时的特性,不需要重新定义实际的实现——NSArray 的方法仍然可以使用。

可以将其自动化并将其归结为仅两个语句,这使其接近支持泛型的语言。我使用WMGenericCollection创建了一个自动化,其中模板作为 C 预处理器宏提供。

导入包含宏的头文件后,您可以创建一个带有两条语句的通用 NSArray:一条用于接口,一条用于实现。您只需提供要存储的数据类型和子类的名称。WMGenericCollection 为NSArrayNSDictionaryNSSet以及它们的可变对应物提供了这样的模板。

于 2013-03-14T18:48:33.363 回答
0

不,Objective-C 目前不支持集合元素的参数类型。

但是,这个主题比问题或现有答案所承认的要复杂。

Objective-C 中集合的参数类型与 C#/Java 中的泛型不同。例如,您不太可能看到 Objective-C 添加了确保添加到集合中的每个对象都是NSArray类型或子类型的功能。相反,Objective-C 可以(并且 IMO 应该)有能力确保集合中的每个对象都符合协议/接口。(即它实现了一组必需的方法)

为什么?

Objective-C 是一种基于协议(接口)兼容性而不是子类型关系的语言。也就是说,如果对象具有所有正确的方法,它们是兼容的,我们不关注或关心它们的实际类型。事实上,在 Obj-C 中查看实际类型是一种非常非常糟糕的做法,并且非常不鼓励这样做。这个概念有时被称为“鸭子打字”,因为如果它像鸭子一样嘎嘎叫,那就是鸭子。我们不在乎它是否真的从某些特定的鸭子那里继承而来。这可以防止您被其他人的实现层次结构所困扰。-- 结果是只要从列表中出来的对象有一个 draw:: 方法就可以了,我们实际上并不关心它是否是某个特定 JimmyDrawableBase 对象的子类。

这不仅使代码更可重用,而且还鼓励稍微不同(更实用?)类型的问题分解,因为您不能依赖从给定基类派生的对象,从而拥有一堆基类强制执行。

我个人认为Obj-C 编译器对PROTOCOL *CONFORMANCE*进行参数检查会很好。也就是说,要使一个 NSMutableArray 需要放置在其中的所有对象符合给定的协议(即具有给定的一组所需的方法)。

有时,即使是这种更灵活的协议一致性检查也会受到动态编程人员的抵制,并且有充分的理由。程序员通常有一种过度指定一致性要求的方法。

例如,您可能需要一个包含符合 NSArray 协议/接口的对象的列表,但实际上您可能调用其中两个方法。这是过度一致性。希望在您的数组中添加兼容项目的人被迫实现大量您实际上没有调用的方法——至少现在还没有(见下)。

Google Go 试图通过推断结构兼容性来解决这个问题。也就是说,如果您对来自列表的项目调用 draw(),那么编译器会确保进入列表的所有内容都包含 draw() 方法。如果它不包含 draw() 方法,则将其放入列表中是编译器错误。这可以防止代码在运行时简单地导致相同的错误发生。问题在于它仅适用于整个程序编译。如果 Google-Go 可以编译模块化 DLL(它不能),那么它会遇到一个问题,即我无法说列表中的对象需要支持三种方法的特定接口,即使我今天不给他们打电话,因为我将来可能会打电话给他们

这两个解决方案之间喜欢权衡和真相。

就个人而言,我希望看到 Objective-C 添加参数协议一致性,所以我可以要求编译器确保特定集合的内容始终符合给定的协议集。

我还希望编译器帮助我避免过度一致性。如果我没有在对象上调用这些协议中的方法,它应该会生成错误/警告告诉我。如果即使我没有使用它们,我也想将它们保留在协议中,我应该为协议中的每个方法显式声明它“将来可能会使用,因此元素都需要现在提供它”。这至少使得过度一致性的过程需要更多的工作,而不是需要更少工作的 Java/C#。

于 2013-08-01T17:30:18.230 回答