I understand that it marks the end of a set of varargs, but why can't it be implemented in such a way that doesn't require the nil?
6 Answers
It all has to do with the C calling ABI.
Consider these methods:
- (id)initWithFormat:(NSString *)format, ...;
+ (id)arrayWithObjects:(id)firstObj, ...;
+ (id)dictionaryWithObjectsAndKeys:(id)firstObject, ...;
The ...
tells the compiler that a variable number of arguments of any type may be present. There is no way for the compiler to know what those types are required to be (the real definitions have markers that help with that).
Now, consider the three methods. All three have vastly different requirements for what might be present in the variable argument list. An array must be a bunch of objects followed by a nil. The dictionary requires a bunch of pairs of objects followed by a nil. Finally, the string method requires a bunch of arguments that match the types in the format string.
All of these behaviors are directly tied to the method being invoked and, if the author of an API decided to go for "hard to use", the behavior of decoding the variable arguments could be modified at runtime, just to make life difficult.
Bottom line: The C ABI doesn't have a syntax that allows for specifying that a method or function takes a variable number of arguments with any kind of set of constraints on the arguments or their termination.
Objective-C could change the rules just for method declarations & invocations, but that wouldn't help with C functions or C++, both of which Objective-C must remain compatible with.

- 18,369
- 7
- 84
- 116

- 162,346
- 23
- 271
- 359
-
It seems like the compiler could transparently add a final nil to [NSArray arrayWithObjects:a, b, c]. That would leave a gotcha that nil would not be permitted as an argument. We could even do this now. People who already include the nil would just get [NSArray arrayWithObjects:a, b, c, nil, nil], which I think is not fatal. – Ken Aug 26 '09 at 10:22
-
2Ken, putting special cases in the compiler for particular classes like NSArray opens a huge can of worms. What if you subclass NSArray? – NSResponder Sep 06 '09 at 14:20
-
@Ken: You can't add `nil` to an `NSArray` anyway (you have to use `NSNull` instead). – mipadi Aug 25 '10 at 20:20
Any varargs function requires someway to know how many parameters are present -- if you don't nil terminate, you would just need something else. In this case the obvious alternative is a length, but then you'd need to update the length argument everytime you changed how many items were in the array, and that could be cumbersome, or worse broken.
I am guessing you have an array that may contain nil?

- 35,755
- 9
- 58
- 55
-
1NSArray can't contain nil — you have to use NSNull to represent it. I'm guessing he's just been bitten by the annoying bugs that can result from forgetting the nil terminator. – Chuck Aug 21 '09 at 01:13
-
It's not as much being bit by forgetting the nil terminator, it's just that I'm used to other languages like Java and Ruby that allow for varargs without a nil terminator. Maybe what I'm asking is why does Obj-C require a nil terminator for varargs? Why can't the compiler just "do the right thing" for [NSArray arrayWithObjects: @"foo", @"bar"]? My question is more of a question of curiosity of why the language requires this. It's an obvious wart when coming from other languages. It's it because that's how varargs for in C functions? – pjb3 Aug 21 '09 at 03:07
-
3I _believe_ objective-c varargs are built on the standard C varargs which doesn't pass argument counts. Conceivably obj-c could have done that back in its original implementation, but now it is bound by ABI compatibility. – olliej Aug 21 '09 at 03:26
-
1Yep, there are no special Objective-C varargs. This is just plain vanilla varargs, with all the annoyances that come along with it. – Chuck Aug 21 '09 at 16:14
-
1OllieJ is correct. Keep in mind that obj-C is a proper superset of C. What you're suggesting is introducing a special case in the compiler where it has to care what class and selector you're using. – NSResponder Aug 21 '09 at 03:28
@bbum gives some great technical details. Here are some thoughts on the practical "why don't they fix it?"
Remember: C is just assembler and Obj-C is just C... It could be redesigned of course, but there's almost no pressure to do so. You still couldn't put nil in an array (that would require a huge change). The compiler now warns you if you forget the nil, so it's not something developers complain a lot about. The trade-off is keeping the language much (much!) simpler than its kin, getting the benefits of decades of C-compiler optimizations and guaranteed compatibility with C code.
One thing that should become clear from @bbum's discussion: NSArray is not a language feature of Objective-C. C-arrays are a language feature, but NSArrays are just another object, no different than the objects you write.

- 286,113
- 34
- 456
- 610
There are various workarounds, but my current favorite is good for apps more than for released frameworks. Accept an NSArray in your method instead of "...", and then fill it with the convenience macro below, which is placed in your prefix file or utility header.
#define $array(objs...) [NSArray arrayWithObjects: objs, nil]
It'll allow for multiple well labeled variable length arguments and you'll free yourself from the archaic pattern of having to use the first argument and then va_list and its brothers in favor of a for-in loop or the many other collection tools available.
[self findMatchingSetAndAlert:@"title" ties:$array(tie1, tie2, tie3) shirts:$array(shirt1, shirt2, shirt3, shirt4)];
If someone knows how to implement a non-nil-delimited list such as stringWithFormat, please let us know! It uses attributes and macros or something designed specifically for formatting, but those are implemented somehow.

- 18,141
- 8
- 79
- 101
-
My answer is rather old and macros are no longer needed as a crutch. Modern Objective C syntax helps a lot, as you can now create an array like `@[@"firstString", @"secondString"]`. We can now worry about types instead of syntax ceremony. – Peter DeWeese Jun 02 '14 at 14:19
You can use now the new Collection Literals (alias Container Literals) in Objective-C. See http://clang.llvm.org/docs/ObjectiveCLiterals.html

- 4,263
- 2
- 24
- 33
The simple reason is that behind the scene it's a for-loop that will continue to take arguments, from the va_list
till it reaches nil
. So the end condition could have been anything, for example the string "stop". But nil
is actually quite smart.
Let's say we have three objects hansel
, gretel
and woodcutter
and create an array of them:
NSArray *startCharacters = [NSArray arrayWithObjects:hansel, gretel, woodcutter, nil];
Now, we realize that woodcutter
never was initiated, so it's nil
, but startCharacters
will still be created with the hansel
and gretel
objects, since when it's reaches woodcutter
it terminates. So the nil-termination in arrayWithObjects:
prevents the app from crashing.
If you don't like to write out nil
you could always create an array like this:
NSArray *startCharacters = @[hansel, gretel, woodcutter];
It's valid, it's shorter, but it will crash if an object is nil
. So the conclusion is that arrayWithObjects:
can still be very useful and you can use it to your advantage.

- 6,356
- 7
- 32
- 47