Cocoa/Objective-C 中有类似通用列表的东西吗?
我真正喜欢 C# 的是通用列表。 只能包含一种类型的对象的列表。 Cocoa/Objective-C 中有类似通用列表的东西吗? 到目前为止,我只知道 NSArray 会获取指向任何对象的指针。
What I really like in C# are generic lists. A list that can contain only one type of objects.
Is there something like a generic list in Cocoa/Objective-C? As far I only know NSArray
who will take a pointer to any object.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
在 Cocoa 应用程序中想要这样通常是设计薄弱的标志。
NSArray
是不可变的,因此它不会“获取指向任何对象的指针”,并且在交给您时可能已经包含正确的对象。 我认为您更担心的是NSMutableArray
,您认为代码的其他部分可能会添加错误类型的对象。 但看看 Cocoa 本身; 在类设计中公开可变数组的情况非常罕见。相反,您通常会公开一个 NSArray 和一些用于修改该数组的方法。 大致如下:
这通常只需通过编译器警告即可阻止插入错误的对象,然后当然您可以在
-addBar:
和-removeBar:
中添加断言如果你也愿意的话。Wanting this in a Cocoa app is often a sign of a weak design.
NSArray
is immutable, so it will not "take a pointer to any object" and presumably already contains the correct objects when handed to you. What I assume you're more worried about is anNSMutableArray
where you think other parts of your code might add the wrong sort of object. But have a look at Cocoa itself; it's incredibly rare to expose a mutable array as part of a class's design.Instead, you generally expose an
NSArray
and a couple of methods for modifying that array. Something along the lines of:This generally stops wrong objects being inserted simply by having a compiler warning, and then of course you can add assertions within
-addBar:
and-removeBar:
if you wish too.Objective-C 不支持泛型编程。 您始终可以使用 Objective-C++ 和 STL 列表。
Objective-C doesn't support generic programming. You could always use Objective-C++ and an STL list.
通用 NSArray 可以通过子类化 NSArray 并用更严格的方法重新定义所有提供的方法来实现。 例如,
必须将
重新定义为
NSArray
仅包含 NSString。 创建的子类可以用作直接替换,并带来许多有用的功能:编译器警告、属性访问、更好的代码创建和 Xcode 中的完成。 所有这些都是编译时功能,无需重新定义实际实现 - NSArray 的方法仍然可以使用。
可以将其自动化并将其简化为仅两个语句,这使其接近支持泛型的语言。 我使用 WMGenericCollection 创建了一个自动化,其中模板作为 C 预处理器宏提供。
导入包含宏的头文件后,您可以创建一个带有两条语句的通用 NSArray:一条用于接口,一条用于实现。 您只需提供要存储的数据类型和子类的名称。 WMGenericCollection 为
NSArray
、NSDictionary
和NSSet
及其可变对应项提供此类模板。Generic NSArrays can be realized by subclassing
NSArray
, and redefining all provided methods with more restrictive ones. For example,would have to be redefined in
as
for an NSArray to contain only NSStrings.
The created subclass can be used as a drop-in replacement and brings many useful features: compiler warnings, property access, better code creation and -completion in Xcode. All these are compile-time features, there is no need to redefine the actual implementation - NSArray's methods can still be used.
It's possible to automate this and boil it down to only two statements, which brings it close to languages that support generics. I've created an automation with WMGenericCollection, where templates are provided as C Preprocessor Macros.
After importing the header file containing the macro, you can create a generic NSArray with two statements: one for the interface and one for the implementation. You only need to provide the data type you want to store and names for your subclasses. WMGenericCollection provides such templates for
NSArray
,NSDictionary
andNSSet
, as well as their mutable counterparts.不,Objective-C 目前不支持集合元素的参数类型。
然而,这个主题比问题或现有答案承认的更复杂。
Objective-C 中集合的参数类型与 C#/Java 中的泛型不同。 例如,您不太可能看到 Objective-C 添加了确保添加到集合中的每个对象都是 NSArray 类型或子类型的功能。 相反,Objective-C 可以(并且 IMO 应该)有能力确保集合中的每个对象都符合协议/接口。 (即它实现了一组必需的方法)
为什么?
Objective-C 是一种基于协议(接口)兼容性而不是子类型关系构建的语言。 也就是说,如果对象具有所有正确的方法,则它们是兼容的,我们不查看或关心它们的实际类型。 事实上,在 Obj-C 中查看实际类型是一种非常非常糟糕的做法,并且非常不鼓励。 这个概念有时被称为“鸭子打字”,因为如果它像鸭子一样嘎嘎叫,那么它就是鸭子。 我们不在乎它是否确实是从某些特定的鸭子继承的。 这可以防止您被其他人的实现层次结构所困扰。 -- 结果是,只要列表中出现的对象有一个可以工作的 draw:: 方法,我们实际上并不关心它是否是某个特定 JimmyDrawableBase 对象的子类。
这不仅使代码更可重用,而且还鼓励稍微不同(更实用?)类型的问题分解,因为您不能依赖从给定基类派生的对象,从而拥有一堆基类强制实施。
我个人认为 Obj-C 编译器对协议 *CONFORMANCE* 进行参数检查会很好。 也就是说,要创建一个 NSMutableArray,要求放置在其中的所有对象都符合给定的协议(即具有一组给定的所需方法)。
有时,甚至这种更灵活的协议一致性检查也会受到动态编程人员的抵制,并且有充分的理由。 程序员常常会过度指定一致性要求。
例如,您可能需要一个包含符合 NSArray 协议/接口的对象的列表,但您实际上可能只调用其中两个方法。 这是过度符合。 希望在数组中添加兼容项的人被迫实现大量您实际上并未调用的方法——至少现在还没有(请参见下文)。
Google Go 试图通过推断结构兼容性来解决这个问题。 也就是说,如果您对列表中的项目调用draw(),那么编译器会确保进入列表的所有内容都包含draw() 方法。 如果它不包含draw()方法,则将其放入列表中会出现编译器错误。 这可以防止代码在运行时简单地导致相同的错误发生。 这样做的问题是它只适用于整个程序编译。 如果 Google-Go 可以编译模块化 DLL(它不能),那么它就会遇到这样的问题:我没有办法说列表中的对象需要支持三种方法的特定接口,即使我今天不会给他们打电话,因为我将来可能会给他们打电话。
在这两种解决方案之间需要权衡和真相。
就我个人而言,我希望看到 Objective-C 添加参数协议一致性,因此我可以要求编译器确保特定集合的内容始终符合给定的协议集。
我还希望编译器帮助我避免过度一致性。 如果我没有在对象上调用这些协议中的方法,它应该生成错误/警告来告诉我这一点。 如果我想将它们保留在协议中,即使我没有使用它们,我应该必须为协议中的每个方法显式声明它“将来可能会使用,因此所有元素现在都需要提供它” ”。 这至少使得过度一致性的过程需要更多的工作,而不是 Java/C# 需要更少的工作。
No, Objective-C does not currently support parametric typing for collection elements.
However, this topic is more complex than the question or existing answers admit..
Parametric-Typing for collections in Objective-C would not be the same as Generics in C#/Java. For example, it is unlikely you would ever see Objective-C add the capability to assure every object added to a collection IS an NSArray type or subtype. Instead, Objective-C could (and IMO should) have the ability to assure every object in a collection CONFORMS to a protocol/interface. (i.e. that it implements a set of required methods)
Why?
Objective-C is a language built on protocol (interface) compatibility, NOT subtyping relationships. That is, objects are compatible if they have all the right methods, we don't look at or care about their actual types. In fact, looking at actual types is a very very bad practice in Obj-C and in highly discouraged. This notion is sometimes called "Duck Typing", because if it quacks like a duck, it's a duck. We don't care if it literally inherited from some specific duck or not. This prevents you from being saddled by someone elses implementation hierarchy. -- The result is that as long as an object coming out of the list has a draw:: method it works, we don't actually care if it is a subclass of some specific JimmyDrawableBase object.
This not only makes code more reusable, but it also encourages a slightly different (more functional?) type of problem decomposition, because you can't rely on objects being derived from a given base class and thus having a bunch of your base-class implementation forced into them.
I personally think it would be nice for the Obj-C compiler to have parametric checking of PROTOCOL *CONFORMANCE*. That is, to make a NSMutableArray which requires all objects placed in it conform to a given protocol (i.e. have a given set of required methods).
Sometimes even this more-flexible protocol-conformance checking is resisted by dynamic programming folks, and with sound reasons. Programmers often have a way of over-specifying conformance requirements.
For example, you might require a list contain objects conforming to the NSArray protocol/interface, but you might ACTUALLY only call two of those methods. This is over-conformance. Someone who wishes to stick a compatible item in your array is forced to implement a ton of methods you are not actually calling -- at least not yet (see next).
Google Go tries to solve this problem by inferring structural compatibility. That is, if you call draw() on items coming out of a list, then the compiler assures everything going into a list contains a draw() method. If it does not contain a draw() method, it's a compiler error to put it into the list. This prevents the code from simply causing the same error to occur at runtime. The problem with this is that it only works for whole-program compilation. If Google-Go could compile modular DLLs (which it can't), then it would run into the problem that there isn't a way for me to say objects in the list need to support a specific interface of three methods, even though I'm not calling them today, because I might call them in the future.
Between those two solution likes the tradeoff and the truth.
Personally, I would like to see Objective-C add parametric protocol conformance, so I could ask the compiler to assure the contents of a particular collection always conform to a given set of protocols.
I would also like the compiler to help me avoid over-conformance. If I'm not calling methods in those protocols on objects, it should generate errors/warnings telling me so. If I want to keep them in the protocol even though I'm not using them, I should have to explicitly make a declaration for each method in the protocol that it "might be used in the future, so elements all need to supply it now". This at least makes the process of over-conformance require MORE work, instead of Java/C# where it requires less work.