在您创建包含C++
或C#
std::vector
时使用C#
和list
等语言时,您明确声明了容器类型创建它:
C ++:
std::vector<MyObject>
C#:
List<MyObject> list = new List<MyObject>();
查看上面的代码,我立即知道这些容器只能包含MyObject
类型的对象,如果我尝试添加一个不属于这种类型的对象,编译器会抱怨。
由于Objective-C是一种动态语言,我们没有编译器的特权警告我们(因为它是完全有效但有潜在危险的事情):
目标-C:
NSDictionary *dict = [[NSDictionary alloc]init];
[dict setValue:[[SomeClass alloc]init] forKey:@"someClass"];
[dict setValue:[[NSMutableString alloc]init] forKey:@"mutableString"];
BOOL classIsSomeClass = [[dict objectForKey:@"someClass"] isKindOfClass:[SomeClass class]];
而是NSDictionary
或NSArray
之类的东西会存储和接受从NSObject
继承的任何类型的对象。我发现这本身非常灵活但我无法确定容器中的对象类型我只能在runtime
知道,而c++
或c#
我知道compile time
1}}只是通过查看代码。
从Apple的Foundation Framework添加,使用和删除容器类(NSArray
,NSSet
,NSDictionary
等对象)时,我是否应该验证容器的内容? ?或者这在所有情况下都没问题,并且验证会损害性能吗?:
NSDictionary *dict = [[NSDictionary alloc]init];
[dict objectForKey:@"someKey"]; // return nil?
答案 0 :(得分:5)
Objective-C的动态消息传递更像是Python或Ruby等动态语言。在这些语言中,标准范例通常被称为“鸭子打字”。换句话说,如果一个对象实例像鸭子一样嘎嘎叫(即响应你正在发送的消息),它就是一只鸭子。在Objective-C中,可以在运行时通过对象继承层次结构之外的许多机制添加方法。因此,询问实例是否响应特定选择器更为常见:
if([obj respondsToSelector:@selector(myMethod)]) {
[obj myMethod];
}
而不是询问obj
是否属于某个类的层次结构。
在大多数情况下,Objective-C开发人员不会进行此检查,除非他们从“未知”模块获取对象实例。相反,我们在很大程度上依赖于编译器警告(Objective-C编译器将警告将消息发送到不确定可以接收该消息的类型)和单元测试。在这种情况下,单元测试确认正确的对象进入集合并且您从集合中获得预期的类型可能会大大减轻您的恐惧。
答案 1 :(得分:2)
似乎是“Objective-C Way”,以避免检查从集合中获取的对象的类型。当然这是好事还是值得商榷的,但我认为这是一个总体主题的一部分,它更倾向于考虑对象响应的消息而不是对象本身。
这方面的一个示例是许多对象响应的各种...Value
(例如stringValue
,intValue
等)消息。另外值得注意的是,id
类型会自动抑制的任何警告,因此某些消息可能无法响应此类消息。
答案 2 :(得分:0)
我想说Objective-C中的模式只是将一种类型的对象存储在容器中 - 而且几乎总是可以确定容器中的内容。这就是为什么实际上很少有人真正花时间检查集合的内容。当我想验证某些内容时,我通常使用isKindOfClass:和一个正确类型的对象来保存集合中的项目。
如果您真的担心因某种原因打字,那么创建一个实现了objectAtIndex的类型化版本的包装类和其他常见的NSArray方法会非常容易 - 请注意,我不是在谈论NSArray的子类或任何其他类型集合,只是一个具有类似消息名称的对象。对于大量使用而言,这类事情可能会有所下降,您可以随时添加一个通过方法来获取支持集合。但是我认为这比它的价值更麻烦,而且不再采用这种语言。
在许多应用程序的实践中,我几乎从未看到“数组中错误的对象类型”出现问题。
现在对于一个接受typeID参数的方法,我更有可能在使用之前检查它的类型 - 因为这些方法往往会涉及更广泛的对象。