Objective-C 中是否存在强类型集合?

我是 Mac/iPhone 编程和 Objective-C 的新手。在 C # 和 Java 中,我们有“泛型”,即集合类,其成员只能是声明的类型。例如,在 C # 中

Dictionary<int, MyCustomObject>

只能包含整数键和 MyCustomObject 类型的值。Objective-C 中是否存在类似的机制?

55531 次浏览

Objective-C 中没有泛型。

来自文件

数组是对象的有序集合。 Cocoa 提供了几个数组类,NSArray、 NSMutableArray (NSArray 的一个子类)和 NSPointerArray。

这个答案已经过时,但仍具有历史价值。至于 Xcode 7,Connor 在6月8日15日的回答更准确。


不,Objective-C 中没有泛型,除非您想在自己的定制集合类中使用 C + + 模板(我强烈反对这种做法)。

Objective-C 将动态类型作为一个特性,这意味着运行时不关心对象的类型,因为所有对象都可以接收消息。当您将一个对象添加到内置集合时,它们仅被视为类型 id。但是不用担心,只要像平常一样向这些对象发送消息就可以了; 它会工作得很好的 (当然,除非集合中的一个或多个对象没有响应您发送的消息)

在 Java 和 C # 等语言中需要泛型,因为它们是强大的静态类型语言。完全不同于 Objective-C 的动态输入特性。

没有,但是为了让它更清晰,你可以用你想要存储的对象类型来注释它,我见过几次这样做,当你需要用 Java 1.4写东西的时候)例如:

NSMutableArray* /*<TypeA>*/ arrayName = ....

或者

NSDictionary* /*<TypeA, TypeB>*/ dictionaryName = ...

Apple 和 GNUStep 框架提供的 Collection 类是半通用的,因为它们假定它们是给定的对象,一些是可排序的,一些是响应特定消息的。对于像 float、 int 等这样的原语,所有的 C 数组结构都是完整的,可以使用,并且有专门的包装器对象用于一般的集合类(例如 NSNumber)。 此外,Collection 类可以被子类化(或者通过类别特别修改)以接受任何类型的对象,但是您必须自己编写所有类型处理代码。 消息可以发送到任何对象,但是如果对象不适合,则应该返回 null,或者应该将消息转发到适当的对象。应该在编译时而不是运行时捕获真实类型错误。在运行时,应该处理或忽略它们。 最后,Object 提供了运行时反射工具来处理棘手的情况和消息响应、特定类型以及可以在对象被发送消息或放入不适当的集合之前对其进行检查的服务。 请注意,不同的库和框架在发送它们没有代码响应的消息时对其对象的行为采用不同的约定,因此 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];

通用 NSArray 可以通过对 NSArray进行子类化,并用更具限制性的方法重新定义所有提供的方法来实现,

- (id)objectAtIndex:(NSUInteger)index

必须重新定义

@interface NSStringArray : NSArray

作为

- (NSString *)objectAtIndex:(NSUInteger)index

对于只包含 NSString 的 NSArray。

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

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

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

示例: List<int>可以通过一个名为 NumberArray的自定义类实现,该类使用以下语句创建:

WMGENERICARRAY_INTERFACE(NSNumber *, // type of the value class
// generated class names
NumberArray, MutableNumberArray)

一旦创建了 NumberArray,就可以在项目中的任何地方使用它。它缺乏 <int>的语法,但是您可以选择自己的命名方案,将这些类标记为模板。

请看:

Https://github.com/tomersh/objective-c-generics

它似乎是一种穷人的泛型,通过重新利用协议检查机制。

在 Xcode 7中,苹果在 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 集合类之外,Objective-C 轻量级泛型被 Swift 忽略。使用轻量级泛型的任何其他类型都被导入到 Swift 中,就好像它们是非参数化的一样。

与 Objective-C API 交互

苹果已经在 XCode 7中将泛型添加到了 ObjecC 中:

@property NSArray<NSDate *>* dates;
- (NSArray<NSDate *> *)datesBeforeDate:(NSDate *)date;
- (void)addDatesParsedFromTimestamps:(NSArray<NSString *> *)timestamps;

看这里: Https://developer.apple.com/library/prerelease/mac/documentation/swift/conceptual/buildingcocoaapps/workingwithcocoadatatypes.html#//apple_ref/doc/uid/tp40014216-ch6-id61

现在梦想成真了——从今天起 Objective-C 中就有泛型了(感谢 WWDC)。 这不是一个笑话-在 官方页面的斯威夫特:

新的语法特性允许您编写更具表现力的代码,同时提高整个语言的一致性。SDK 采用了新的 Objective-C 特性,比如泛型和空性注释,使 Swift 代码更加清晰和安全。这里只是 Swift 2.0增强的一个例子。

想象一下,证明了这一点:Objective-C generics

这是在 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];

小心那些 ABC。

我写了一篇关于泛型的博客文章 这边

我想要贡献的是 泛型可以添加到任何类中,而不仅仅是苹果公司指出的收集类。

我已经成功地添加了各种类,因为它们的工作原理与苹果的系列完全一样。也就是说。编译时间检查、代码完成、允许删除强制转换等。

好好享受吧。