断言在Objective C中每个对象的创建都是必要的吗?

时间:2011-03-27 17:06:12

标签: objective-c coding-style

我最近阅读了Apple的开发者技术支持大师Quinn“The Eskimo!”撰写的Apple MVCNetworking示例代码。该示例是非常好的学习经验,我认为这是iOS开发的最佳开发实践。 来自JVM语言的让我感到惊讶的是这样的非常频繁的断言:

syncDate = [NSDate date];
assert(syncDate != nil);

和此:

photosToRemove = [NSMutableSet setWithArray:knownPhotos];
assert(photosToRemove != nil);

和此:

photoIDToKnownPhotos = [NSMutableDictionary dictionary];
assert(photoIDToKnownPhotos != nil);

真的有必要吗?这种编码风格值得模仿吗?

5 个答案:

答案 0 :(得分:4)

如果你习惯了Java,这可能看起来很奇怪。您希望对象创建消息在失败时抛出异常,而不是返回nil。然而,虽然Objective-C on Mac OS X has support for exception handling;它是一个可选功能,可以使用编译器标志打开/关闭。编写标准库,以便可以在不启用异常处理的情况下使用它们:因此消息通常返回nil来指示错误,有时还需要您传递指向NSError*变量的指针。 (这是针对Mac开发的,我不确定你是否甚至可以为iOS启用异常处理支持,考虑到你也无法打开iOS的垃圾收集。)

文档“Objective-C编程语言”中的section "Handling Initialization Failure"解释了Objective-C程序员如何处理对象初始化/创建中的错误:即返回nil

[NSData dataWithContentsOfFile: path]之类的内容肯定会返回nil:该方法的文档明确说明了这一点。但我老实说不确定[NSMutableArray arrayWithCapacity: n]之类的东西是否会返回nil。当可能时,我能想到的唯一情况是应用程序内存不足。但在那种情况下,我希望通过尝试分配更多内存来中止应用程序。我没有检查过这个,在这种情况下它可能会返回nil。虽然在Objective-C中你常常safely send messages to nil,但这仍然会导致不良结果。例如,您的应用可能会尝试制作NSMutableArray,而是获取nil,然后愉快地继续向addObject:发送nil并将空文件写入磁盘而不是一个具有预期的数组元素。因此,在某些情况下,最好明确检查消息的结果是否为nil。是否有必要在每个对象创建时进行,就像你引用的程序员正在做的那样,我不确定。比抱歉更安全吗?

编辑:我想补充一点,当检查时,对象创建成功有时可能是一个好主意,断言它可能不会是最好的主意。您还希望在应用程序的发行版中检查它,而不仅仅是在调试版本中。否则它有点无法检查它,因为你不希望应用程序最终用户,例如,因为[NSMutableArray arrayWithCapacity: n]返回nil并且应用程序继续发送消息而结束空文件nil返回值。可以使用编译器标志从发布版本中删除断言(使用assertNSAssert);默认情况下,Xcode在“Release”配置中似乎没有包含这些标志。但是如果您想使用这些标志来删除其他一些断言,那么您也将删除所有“对象创建成功”检查。

编辑:经过进一步反思,似乎比我第一次认为[NSMutableArray arrayWithCapacity: n]返回nil时更合理,而不是在没有足够内存可用时中止应用程序。基本C malloc也不会中止,但在没有足够的内存可用时返回NULL指针。但我还没有在alloc和类似方法的Objective-C文档中发现任何明确的提及。

编辑:上面我说我不确定在每次创建对象时都需要检查nil。但它不应该。这正是Objective-C允许向nil发送消息的原因,nil然后返回0(或nil或类似的东西,具体取决于消息定义):这样,{{1}可以通过您的代码传播,有点类似于异常,因此您必须在可能返回它的每条消息上显式检查nil。但最好在不希望它传播的位置检查它,例如在编写文件,与用户交互等时,或者在向nil发送消息的结果中未定义(如documentation on sending messages to nil中所述)。我倾向于说这就像异常传播和处理的“穷人”版本,虽然不是每个人都同意后者更好;但是nil并没有告诉你发生错误的原因,你很容易忘记检查是否需要这样的检查。

答案 1 :(得分:3)

建议您阅读Defensive Programming

上的这篇文章

答案 2 :(得分:2)

烨。我认为这是一个好主意..一旦引入变量,它有助于过滤掉边缘情况(内存不足,输入变量为空/零)。虽然由于开销,我不确定对速度的影响!

答案 3 :(得分:2)

我想这是个人选择的问题。通常断言用于调试目的,以便在不满足条件时应用程序在断言点崩溃。您通常希望在应用程序版本上删除它们。

我个人懒得在你所展示的每个代码块周围放置断言。我认为这有点过于偏执了。在涉及某些不确定性的情况下,断言可能非常方便。

答案 4 :(得分:1)

我有also asked this on Apple DevForums。根据奎因“爱斯基摩人!” (MVCetworking样本的作者)它是matter of coding style and his personal preference

  

我使用了很多断言,因为我讨厌调试。 (...)

     

请记住,我是在传统Mac OS中长大的,其中一个流氓指针可能会导致整个机器崩溃(类似于当前系统上的内核编程)。在那个世界中,尽快找到你的错误非常重要。很多断言帮助你这样做。

     

此外,即使在今天,我仍然花了很多时间处理网络程序。由于涉及异步,调试网络程序很难。断言对此有帮助,因为它们会在程序运行时不断检查程序的状态。

     

但是,我认为你对+[NSDate date]这样的东西有一个有效的观点。返回零的可能性很低。 断言纯粹来自习惯。但我认为这种习惯的成本(一些额外的打字,学会忽略断言)与收益相比很小

从此我收集到断言每个对象创建成功并非绝对必要。

  • 断言对于记录方法中的前提条件,在开发期间,作为其他维护者(包括未来的自我)的设计辅助非常有价值。 我个人更喜欢替代风格 - 使用TDD / BDD实践分离规范和实现。

  • 由于Objective C的动态特性,断言可用于仔细检查方法参数的运行时类型

    assert([response isKindOfClass:[NSHTTPURLResponse class]]);
    

我确信断言有更好的用法。适度的一切......