我是 Mac/iPhone 编程和 Objective-C 的新手。在 C# 和 Java 中,我们有“泛型”,即其成员只能是声明类型的集合类。例如,在 C#
Dictionary<int, MyCustomObject>
只能包含整数键和 MyCustomObject 类型的值。Objective-C 中是否存在类似的机制?
我是 Mac/iPhone 编程和 Objective-C 的新手。在 C# 和 Java 中,我们有“泛型”,即其成员只能是声明类型的集合类。例如,在 C#
Dictionary<int, MyCustomObject>
只能包含整数键和 MyCustomObject 类型的值。Objective-C 中是否存在类似的机制?
在 Xcode 7 中,Apple 向 Objective-C 引入了“轻量级泛型”。在 Objective-C 中,如果类型不匹配,它们将生成编译器警告。
NSArray<NSString*>* arr = @[@"str"];
NSString* string = [arr objectAtIndex:0];
NSNumber* number = [arr objectAtIndex:0]; // Warning: Incompatible pointer types initializing 'NSNumber *' with an expression of type 'NSString *'
而在 Swift 代码中,它们会产生编译器错误:
var str: String = arr[0]
var num: Int = arr[0] //Error 'String' is not convertible to 'Int'
轻量级泛型旨在与 NSArray、NSDictionary 和 NSSet 一起使用,但您也可以将它们添加到您自己的类中:
@interface GenericsTest<__covariant T> : NSObject
-(void)genericMethod:(T)object;
@end
@implementation GenericsTest
-(void)genericMethod:(id)object {}
@end
Objective-C 的行为会像以前一样,带有编译器警告。
GenericsTest<NSString*>* test = [GenericsTest new];
[test genericMethod:@"string"];
[test genericMethod:@1]; // Warning: Incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString *'
但 Swift 会完全忽略通用信息。(在 Swift 3+ 中不再适用。)
var test = GenericsTest<String>() //Error: Cannot specialize non-generic type 'GenericsTest'
除了这些 Foundation 集合类之外,Swift 忽略了 Objective-C 轻量级泛型。任何其他使用轻量级泛型的类型都被导入到 Swift 中,就好像它们没有参数化一样。
这个答案已经过时,但仍然具有历史价值。从 Xcode 7 开始,Connor 在 15 年 6 月 8 日的回答更加准确。
不,Objective-C 中没有泛型,除非您想在自己的自定义集合类中使用 C++ 模板(我强烈反对)。
Objective-C 具有动态类型作为特性,这意味着运行时不关心对象的类型,因为所有对象都可以接收消息。当您将对象添加到内置集合时,它们只是被视为 type id
。但别担心,像往常一样向那些对象发送消息;它会正常工作(当然,除非集合中的一个或多个对象不响应您发送的消息)。
Java 和 C# 等语言需要泛型,因为它们是强大的静态类型语言。与 Objective-C 的动态类型功能完全不同。
不,但是为了更清楚,你可以用你想要存储的对象的类型来评论它,当你现在需要在 Java 1.4 中编写一些东西时,我已经看到过几次这样做了)例如:
NSMutableArray* /*<TypeA>*/ arrayName = ....
或者
NSDictionary* /*<TypeA, TypeB>*/ dictionaryName = ...
这是在 Xcode 7 中发布的(终于!)
请注意,在 Objective C 代码中,它只是一个编译时检查;仅将错误的类型放入集合或分配给类型化的属性不会出现运行时错误。
宣布:
@interface FooClass <T> : NSObject
@property (nonatomic) T prop;
@end
采用:
FooClass<NSString *> *foo = [[FooClass alloc] init];
NSArray<FooClass<NSString *> *> *fooAry = [NSArray array];
小心那些*
s。
Objective-C 中没有泛型。
数组是对象的有序集合。Cocoa 提供了几个数组类,NSArray、NSMutableArray(NSArray 的子类)和 NSPointerArray。
Apple 在 XCode 7 中为 ObjC 添加了泛型:
@property NSArray<NSDate *>* dates;
- (NSArray<NSDate *> *)datesBeforeDate:(NSDate *)date;
- (void)addDatesParsedFromTimestamps:(NSArray<NSString *> *)timestamps;
通用 NSArrays 可以通过子类化来实现NSArray
,并用更严格的方法重新定义所有提供的方法。例如,
- (id)objectAtIndex:(NSUInteger)index
将不得不重新定义
@interface NSStringArray : NSArray
作为
- (NSString *)objectAtIndex:(NSUInteger)index
一个 NSArray 只包含 NSStrings。
创建的子类可以用作替代品,并带来许多有用的功能:编译器警告、属性访问、更好的代码创建和 Xcode 中的补全。所有这些都是编译时的特性,不需要重新定义实际的实现——NSArray 的方法仍然可以使用。
可以将其自动化并将其归结为仅两个语句,这使其接近支持泛型的语言。我使用WMGenericCollection创建了一个自动化,其中模板作为 C 预处理器宏提供。
导入包含宏的头文件后,您可以创建一个带有两条语句的通用 NSArray:一条用于接口,一条用于实现。您只需提供要存储的数据类型和子类的名称。WMGenericCollection 为NSArray
、NSDictionary
和NSSet
以及它们的可变对应物提供了这样的模板。
一个例子:List<int>
可以通过一个名为 的自定义类来实现NumberArray
,它是使用以下语句创建的:
WMGENERICARRAY_INTERFACE(NSNumber *, // type of the value class
// generated class names
NumberArray, MutableNumberArray)
创建NumberArray
后,您可以在项目中的任何地方使用它。它缺少 的语法<int>
,但您可以选择自己的命名方案将它们标记为类作为模板。
现在梦想成真了——从今天开始,Objective-C 中就有泛型(感谢 WWDC)。这不是玩笑——在Swift的官方页面上:
新的语法功能让您可以编写更具表现力的代码,同时提高整个语言的一致性。SDK 采用了新的 Objective-C 特性,例如泛型和可空性注释,以使 Swift 代码更加干净和安全。这里只是 Swift 2.0 增强功能的一个示例。
和证明这一点的图像:
只想跳到这里。我在这里写了一篇关于泛型的博客文章。
我想贡献的是泛型可以添加到任何类中,而不仅仅是 Apple 指示的集合类。
我已经成功地将它们添加到各种类中,因为它们的工作方式与 Apple 的集合完全相同。IE。编译时检查、代码完成、启用强制类型转换等。
享受。
Apple 和 GNUStep 框架提供的 Collections 类是半通用的,因为它们假定给定对象,有些是可排序的,有些是响应某些消息的。对于浮点数、整数等原语,所有的 C 数组结构都是完整的并且可以使用,并且有特殊的包装器对象供它们在通用集合类中使用(例如 NSNumber)。此外,可以对 Collection 类进行子类化(或通过类别进行专门修改)以接受任何类型的对象,但您必须自己编写所有类型处理代码。消息可以发送到任何对象,但如果它不适合该对象,则应返回 null,或者应将消息转发到适当的对象。真正的类型错误应该在编译时被捕获,而不是在运行时。在运行时,它们应该被处理或忽略。最后,Objc 提供运行时反射工具来处理棘手的情况和消息响应、特定类型和服务,可以在对象被发送消息或放入不适当的集合之前对其进行检查。请注意,不同的库和框架采用不同的约定来确定其对象在发送没有代码响应的消息时的行为方式,因此 RTFM。除了玩具程序和调试版本之外,大多数程序不应该崩溃,除非它们真的搞砸了并试图将坏数据写入内存或磁盘,执行非法操作(例如除以零,但你也可以抓住它),或者访问禁止使用系统资源。Objective-C 的活力和运行时间允许事情优雅地失败,应该内置到你的代码中。(提示)如果您在函数的通用性方面遇到问题,尝试一些特异性。用特定类型重写函数并让运行时选择(这就是它们被称为选择器的原因!)在运行时适当的成员函数。
Example:
-(id) sort (id) obj; // too generic. catches all.
// better
-(id) sort: (EasilySortableCollection*) esc;
-(id) sort: (HardToSortCollection*) hsc;
...
[Sorter sort: MyEasyColl];
[Sorter sort: MyHardColl];