我是 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 中是否存在类似的机制?
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 *'
var test = GenericsTest<String>() //Error: Cannot specialize non-generic type 'GenericsTest'
MyClass <Foo: id<Bar>>
,那么你的 Swift 代码将假定值是你约束的类型,这为你提供了一些可用的东西。然而,MyClass
的专门子类将会忽略它们的专门类型(被视为通用的 MyClass
)。请参阅 https://github.com/bgerstle/LightweightGenericsExample。 - Brian Gerstle此回答已过时,但仍保留历史价值。从Xcode 7开始,Connor在2015年6月8日的回答更准确。
不,除非您想在自己的自定义集合类中使用C++模板(我强烈不建议这样做),Objective-C中没有泛型。
Objective-C具有动态类型作为特性,这意味着运行时不关心对象的类型,因为所有对象都可以接收消息。当您将对象添加到内置集合中时,它们只被视为类型为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];
小心那些*
。
在XCode 7中,苹果为ObjC添加了泛型:
@property NSArray<NSDate *>* dates;
- (NSArray<NSDate *> *)datesBeforeDate:(NSDate *)date;
- (void)addDatesParsedFromTimestamps:(NSArray<NSString *> *)timestamps;
Objective-C 没有泛型。
数组是对象的有序集合。Cocoa 提供了多个数组类,包括 NSArray、NSMutableArray(NSArrary 的子类)和 NSPointerArray。
通过子类化 NSArray
并重新定义所有提供的方法以更加严格的方式,可以实现通用的 NSArrays
。例如,
- (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>
的语法,但您可以选择自己的命名方案将其标记为类模板。我想插入一句话。我在这里写了一篇关于泛型的博客文章。
我想贡献的是泛型可以添加到任何类中,而不仅仅是像苹果所说的集合类。
我已经成功地将它们添加到各种类中,因为它们与苹果的集合类完全相同。即编译时检查、代码完成、使转换操作失效等。
享受吧。