为什么NSArray arrayWithObjects需要终止nil?

时间:2009-08-21 00:31:49

标签: objective-c

我明白它标志着一组varargs的结束,但为什么不能以不需要nil的方式实现呢?

6 个答案:

答案 0 :(得分:17)

这一切都与C呼叫ABI有关。

考虑以下方法:

- (id)initWithFormat:(NSString *)format, ...;
+ (id)arrayWithObjects:(id)firstObj, ...;
+ (id)dictionaryWithObjectsAndKeys:(id)firstObject, ...;

...告诉编译器可能存在任意类型的可变数量的参数。编译器无法知道这些类型需要什么(真正的定义具有帮助它的标记)。

现在,考虑三种方法。这三者对变量参数列表中可能存在的内容有着截然不同的要求。数组必须是一堆后跟nil的对象。字典需要一堆对象后跟一个零。最后,string方法需要一堆与格式字符串中的类型匹配的参数。

所有这些行为都与被调用的方法直接相关,如果API的作者决定“难以使用”,解码变量参数的行为可以在运行时修改,只是为了让生活变得更好困难的。

底线:C ABI没有允许指定方法或函数采用可变数量的参数以及参数或其终止的任何类型的约束的语法。

Objective-C可以改变方法声明和规则的规则。调用,但这对C函数或C ++没有帮助,这两者都与Objective-C保持兼容。

答案 1 :(得分:3)

任何varargs函数都需要知道有多少参数存在 - 如果你没有终止,你只需要其他东西。在这种情况下,显而易见的替代方法是长度,但是每次更改数组中的项目数时,您都需要更新长度参数,这可能很麻烦,或者更糟糕。

我猜你有一个可能包含nil的数组?

答案 2 :(得分:2)

@bbum提供了一些很棒的技术细节。以下是关于实际“他们为什么不修复它?”的一些想法。

记住:C只是汇编程序而Obj-C只是C ......当然可以重新设计,但几乎没有压力要这样做。你仍然不能把nil放在一个数组中(这需要一个巨大的变化)。编译器现在会警告你,如果你忘记了nil,那么这不是开发人员抱怨的事情。这种权衡使得语言比其亲属更容易(更多!),获得数十年C编译器优化的好处,并保证与C代码的兼容性。

@ bbum的讨论应该清楚一点:NSArray不是Objective-C的语言特性。 C数组是一种语言特性,但NSArrays只是另一个对象,与您编写的对象没有什么不同。

答案 3 :(得分:2)

有各种解决方法,但我目前最喜欢的应用程序比发布的框架更有用。在您的方法中接受NSArray而不是“...”,然后使用下面的方便宏填充它,该宏放在您的前缀文件或实用程序标题中。

#define $ array(objs ...)[NSArray arrayWithObjects:objs,nil]

它将允许多个标记良好的可变长度参数,你将摆脱必须使用第一个参数的古老模式,然后va_list及其兄弟支持for-in循环或许多其他集合可用的工具。

[self findMatchingSetAndAlert:@“title”tie:$ array(tie1,tie2,tie3)shirt:$ array(shirt1,shirt2,shirt3,shirt4)];

如果有人知道如何实现非零分隔列表,例如stringWithFormat,请告诉我们!它使用属性和宏或专门为格式化设计的东西,但这些是以某种方式实现的。

答案 4 :(得分:1)

您现在可以在Objective-C中使用新的Collection Literals(别名Container Literals)。见http://clang.llvm.org/docs/ObjectiveCLiterals.html

答案 5 :(得分:0)

一个简单的原因是,在幕后的是一个for循环,它将继续接受参数,从va_list直到到达nil。因此结束条件可能是任何东西,例如字符串“ stop”。但是nil实际上很聪明。

假设我们有三个对象hanselgretelwoodcutter并创建它们的数组:

NSArray *startCharacters = [NSArray arrayWithObjects:hansel, gretel, woodcutter, nil];

现在,我们意识到woodcutter从未启动,因此它是nil,但是startCharacters仍将由hanselgretel对象创建,因为到达woodcutter会终止。因此,arrayWithObjects:中的nil终止可防止应用崩溃。

如果您不希望写出nil,则可以始终创建如下数组:

NSArray *startCharacters = @[hansel, gretel, woodcutter];

有效,较短,但是如果对象为nil,它将崩溃。因此结论是arrayWithObjects:仍然非常有用,您可以利用它来发挥自己的优势。