【问题标题】:Should I verify objects inside Foundation API containers?我应该验证 Foundation API 容器中的对象吗?
【发布时间】:2010-08-06 14:47:01
【问题描述】:

C++C# 等语言中,当您创建std::vectorC# 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]];

取而代之的是NSDictionaryNSArray 之类的东西将存储和接受从NSObject 继承的任何类型的对象。我发现这本身非常灵活,但我无法确定容器中的对象类型,我只能在 runtime 真正知道,而对于 c++c#,我在 compile time 知道这一点,只需查看代码。

从 Apple 的 Foundation Framework 添加、使用和删除容器类(NSArrayNSSetNSDictionary 等)的对象时,我是否应该验证容器的内容?或者这在所有情况下都可以吗?验证是否会严重影响性能?:

NSDictionary *dict = [[NSDictionary alloc]init];
[dict objectForKey:@"someKey"];    // return nil?

【问题讨论】:

  • 明确一点,NSDictionary 不会响应initWithCapacity: NSMutableDictionary。 objectForKey: 在发送到有效的 NSDictionary 对象时永远不会崩溃,即使键不存在或字典的键属于不同类型。它只会返回nil
  • @Art:你说得对,我应该在发布之前将我的代码放入编译器中。

标签: iphone objective-c cocoa macos core-foundation


【解决方案1】:

Objective-C 的动态消息传递更像 Python 或 Ruby 等动态语言。在这些语言中,标准范式通常被称为“鸭式打字”。换句话说,如果一个对象实例像鸭子一样嘎嘎叫(即响应您发送的消息),它就是一只鸭子。在 Objective-C 中,可以在运行时通过多种机制在对象继承层次结构之外添加方法。因此,询问实例是否响应特定选择器更为常见:

if([obj respondsToSelector:@selector(myMethod)]) {
  [obj myMethod];
}

而不是询问obj是否属于某个类的层次结构。

在大多数情况下,Objective-C 开发人员不会进行此检查,除非他们从“未知”模块获取对象实例。相反,我们严重依赖编译器警告(Objective-C 编译器将警告发送消息给它不确定可以接收该消息的类型)和单元测试。在这种情况下,进行单元测试以确认正确的对象正在进入集合并且您从集合中获得预期的类型可能会大大减轻您的恐惧。

【讨论】:

  • 我以前听过duck typing 的描述,但你的解释让我明白了。谢谢
  • 一般来说,如果您在非常有限的模式(例如委托)之外使用respondsToSelector:,或者您使用的是isKindOfClass:,那么您的应用程序设计远远超出了目标规范-C.
  • @bbum:Obj-C 中的多线程怎么样?在拨打[NSThread detachNewThreadSelector:@selector(myThreadMainMethod:) 之前拨打respondsToSelector: 对我来说似乎是个好主意。
  • @Brock 我宁愿我的代码崩溃也不愿神秘地不默默地做一些动作。如果你真的想走那条路,最好做if (! [o respondsToSelector: @selector(myThreadMainMethod:)]) abort();
  • @bbum:我同意,但这是一个权衡。我总是坚持至少一个DebugLog() 来通知自己没有按预期工作的情况。我宁愿我的应用程序有一个小错误,而不是让我的用户崩溃。也许这是错误的方法,但我认为我的用户宁愿它不会崩溃。欢迎提出意见。
【解决方案2】:

避免检查从集合中获取的对象的类型似乎确实是“Objective-C 方式”。当然,这是否好是值得商榷的,但我认为这是更倾向于考虑对象响应的消息而不是对象本身的一般主题的一部分。

这方面的一个例子是许多对象响应的各种...Value(例如stringValueintValue等)消息。另外值得注意的是id 类型会自动抑制某某可能不会响应某某消息 种类的任何警告。

【讨论】:

    【解决方案3】:

    我想说的是,Objective-C 中的模式是只将一种类型的对象存储在容器中——而且几乎总是你可以确定容器中的内容。这就是为什么实际上很少有人真正花时间检查集合的内容。当我确实想验证某事时,我通常使用 isKindOfClass: 和一个正确类型的对象来保存集合中的一个项目。

    如果您出于某种原因真的担心打字,那么创建一个实现 objectAtIndex: 和其他常见 NSArray 方法的类型化版本的包装类将非常容易 - 请注意,我不是在谈论 NSArray 的子类或任何其他集合,只是一个具有相似消息名称的对象。这种东西可以用于很多用途,你总是可以添加一个通过方法来获得支持集合。但我认为这比它的价值更麻烦,并且远离沟壑拥抱语言。

    在很多很多应用程序的实践中,我几乎从未见过“数组中的对象类型错误”成为问题。

    现在对于接受 typeID 参数的方法,我更有可能在使用前检查类型 - 因为这些方法往往会接受更广泛的对象。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-12-15
      • 2021-03-19
      • 2015-04-08
      • 2013-09-07
      • 2013-08-06
      • 1970-01-01
      相关资源
      最近更新 更多