我是Mac / iPhone编程和Objective-C的新手。在C#和Java中,我们有“泛型”,集合类的成员只能是声明的类型。例如,在C#
中 Dictionary<int, MyCustomObject>
只能包含整数和值类型为MyCustomObject的键。 Objective-C中是否存在类似的机制?
答案 0 :(得分:209)
在Xcode 7中,Apple推出了轻量级的Generics&#39;到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'
Lightweight Generics旨在与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中,就好像它们是未参数化的一样。
答案 1 :(得分:91)
这个答案已经过时,但仍然具有历史价值。从Xcode 7开始,Connor从2015年6月8日开始的回答更准确。
不,除非您想在自己的自定义集合类中使用C ++模板,否则Objective-C中没有泛型(我强烈反对)。
Objective-C将动态类型作为一项功能,这意味着运行时不关心对象的类型,因为所有对象都可以接收消息。将对象添加到内置集合时,它们只是被视为类型id
。但不要担心,只需像正常那样向这些对象发送消息;它将正常工作(除非集合中的一个或多个对象当然不响应您发送的消息)。
Java和C#等语言需要泛型,因为它们是强大的静态类型语言。与Objective-C的动态打字功能完全不同的球赛。
答案 2 :(得分:11)
不,但为了更清楚,你可以用你想要存储的对象类型来评论它,我已经看到这样做了几次你现在需要在Java 1.4中写一些东西),例如:
NSMutableArray* /*<TypeA>*/ arrayName = ....
或
NSDictionary* /*<TypeA, TypeB>*/ dictionaryName = ...
答案 3 :(得分:6)
Objective-C中没有泛型。
数组是有序的对象集合。 Cocoa提供了几个数组类,NSArray,NSMutableArray(NSArray的子类)和NSPointerArray。
答案 4 :(得分:6)
Apple已在XCode 7中向ObjC添加了泛型:
@property NSArray<NSDate *>* dates;
- (NSArray<NSDate *> *)datesBeforeDate:(NSDate *)date;
- (void)addDatesParsedFromTimestamps:(NSArray<NSString *> *)timestamps;
答案 5 :(得分:5)
This was released in 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];
小心那些*
。
答案 6 :(得分:4)
通用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>
的语法,但您可以选择自己的命名方案,将它们标记为类作为模板。
答案 7 :(得分:2)
答案 8 :(得分:2)
现在梦想成真 - 从今天开始,Objective-C中存在泛型(感谢WWDC)。 这不是一个笑话 - 关于Swift的official page:
新的语法功能可让您编写更具表现力的代码,同时提高整个语言的一致性。 SDK采用了新的Objective-C功能,例如泛型和可空性注释,使Swift代码更清晰,更安全。这里只是Swift 2.0增强功能的一个示例。
证明这一点的图像:
答案 9 :(得分:2)
只想跳进这里。我撰写了一篇关于泛型的博客文章over here。
我想要贡献的是泛型可以添加到任何类,而不仅仅是Apple指示的集合类。
我已成功添加到各种课程中,因为它们的工作方式与Apple的系列完全相同。即。编译时检查,代码完成,删除强制转换等等。
享受。
答案 10 :(得分:-2)
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];