Why does NSArray arrayWithObjects require a terminating nil?

前端 未结 6 820
时光取名叫无心
时光取名叫无心 2020-12-03 01:29

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条回答
  • 2020-12-03 02:21

    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.

    0 讨论(0)
  • 2020-12-03 02:27

    You can use now the new Collection Literals (alias Container Literals) in Objective-C. See http://clang.llvm.org/docs/ObjectiveCLiterals.html

    0 讨论(0)
  • 2020-12-03 02:28

    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?

    0 讨论(0)
  • 2020-12-03 02:32

    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.

    0 讨论(0)
  • 2020-12-03 02:33

    @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.

    0 讨论(0)
  • 2020-12-03 02:34

    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.

    0 讨论(0)
提交回复
热议问题