8 ms·
nil, Nil, NULL, NSNull
- mikeash 14y agoGood article overall. However, I must object to this bit: "C represents nothing as 0 for primitive value" That's not true. There is no "nothing" for integer types, and floats either also lack "nothing", or have NaN as their "nothing", depending on your perspective. One of the (many) annoying features of C-style NULL is that it means that some types are effectively option types while other types aren't, and there's no decent way to make them so. An NSString* is inherently "pointer to NSString or nothing", but an int is always an int, and I have to either carve out a special value to mean "nothing" (e.g. -1 used as the error return from a lot of UNIX syscalls) or use a separate flag.
- matttthompson 14y agoTo clarify: I mean to say that 0 represents "nothing" in C in the same way that it does in math. This is one of the things I love about C--the ability to exploit the properties of numbers directly, rather than needing to build abstractions around them. Bitmasks, for example, are one of my favorite things ever. But yeah, point well-taken. Perhaps I'll take the opportunity to wax on the other special values used in Foundation & CoreFoundation sometime.
- deleted 14y ago[deleted]
- mikeash 14y ago0 isn't really "nothing", though. It's special, in that it's the additive identity of a lot of useful sets of numbers, but it's definitely something. An integer containing 0 is completely different from a pointer containing nil, conceptually speaking. To really have "nothing", you could use an int*, where a NULL pointer means "nothing", and a valid pointer means it contains a value. Or something more efficient with e.g. a separate flag. But in any case there's no real native support in the language for the concept.
- deleted 14y ago[deleted]
- mpweiher 14y agoStep back for a second and think about what the number 0 means. If I give you 0 dollars, what have I given you? Or go to Wikipedia: 'The wThe word zero came via French zéro from Venetian zero, which (together with cypher) came via Italian zefiro from Arabic صفر, ṣafira = "it was empty", ṣifr = "zero", "nothing". ord zero came via French zéro from Venetian zero, which (together with cypher) came via Italian zefiro from Arabic صفر, ṣafira = "it was empty", ṣifr = "zero", "nothing".'
- mikeash 14y agoI'm well aware of both the meaning and etymology of the word. Now, you should step back for a second and think about what the value 0 means in a program. Take the following function: int secondsUntilNextEvent() Does 0 mean "the next event is immediate", or does it mean "there is no interval because there is no next event"? 0 here does not necessarily mean "nothing". An option type gives you a different state for "there is no value here, at all". Putting a zero into an int certainly does not signify "no value". It signifies a value, and that value is zero. What number is greater than three but less than one? Nothing. There is no such number. "0" is not a correct answer.
- mpweiher 14y agoGlad you are aware that in at least some contexts, zero really is nothing, rather than '0 isn't really "nothing", though', as you claimed earlier. You write: 'Putting a zero into an int certainly does not signify "no value". It signifies a value, and that value is zero.' Again, the way you write this makes it seem like you say that this has always been the case and is the only way it could be, when in fact this idea of zero being "just another value" is a fairly recent one. And the way our machines handle scalar values (and where what you write is largely true) is even more recent and more of a special case. 'What number is greater than three but less than one? Nothing. There is no such number. "0" is not a correct answer.' 'Nothing' is also not the correct answer. What you have is a contradiction, which is not nothing. Or you could talk about the possible results as sets, in which case you have the empty set, which is also not nothing. It does have the cardinality 0, though. "The empty set is not the same thing as nothing; rather, it is a set with nothing inside it and a set is always something. " Of course, the empty set can be used to signify "nothing", just as the number zero can. "The number zero is sometimes used to denote nothing. The empty set contains no elements." Again, not arguing that you can't have contexts in which "nothing" and "zero" are distinct. Just pointing out that those contexts are hardly universal enough to justify a statement saying that nothing really isn't zero. Things are a bit more complicated and less clear-cut than that. Which is why I always liked C's and Objective-C's somewhat loose but intuitively (for me!) workable handling of NULL, 0, nil, false etc.
- shock-value 14y agoDefinitely spot on regarding 0 not representing 'nothing'. However, 'int *' is inherently "pointer to int or nothing" (in the same style as your NSString example), so I'm not sure I agree with your assertion that some types are option types while some aren't in C. Any type can be an option type (in your sense of the term, again a la your NSString example) by making it into a pointer. Similarly, using an NSString without a pointer (which might not be possible in ObjC, but definitely possible for regular C/C++ objects) makes it not optional, like the 'int'.
- jerf 14y agoint and pointer to int are two different types. Pointer-to-int being an option type does not make int an option type. So there are still option types and non-option types. (Using the prevailing terminology. I'm not sure I'm comfortable with this use of the term "option type", but I'm rolling with it for now.)
- mikeash 14y agoPrecisely. It's not the case that "any type can be an option type" as shock-value stated. Rather, in C, all pointer types are option types whether you want them to be or not, and all other types are not.
- SeanLuke 14y ago> In other languages, like C++, this would crash your program, but in Objective-C, invoking a method on nil returns a zero value. This greatly simplifies expressions, as it obviates the need to check for nil before doing anything: // For example, this expression... if (name != nil && [name isEqualToString:@"Steve"]) { ... } // ...can be simplified to: if ([name isEqualToString:@"steve"]) { ... } Wow, that is some bad, bad code. Consider this snippet instead: if ([name isDifferentFromString:@"bob"]) { ... }
- tyler-dodge 14y agoThat seems like a naming decision, I think it'd make more sense to have an equals selector than a not equals selector in objective c. Plus isEqualToString: is already part of NSString
- efnx 14y agoThat's not bad code, you must be sarcastic. I've never heard of the method isDifferentFromString: and besides, the method name suggests it's the same thing as isEqualToString: approached from the other side. Are you referring to the capitalization mistake in @"steve"? When using sarcasm keep in mind there's no tone in text ;)
- optymizer 14y ago<sarcasm>Well, if you've never heard of it then it cannot exist and that invalidates his example, right? </sarcasm> I'm not saying the example is correct, I'm pointing out that "I've never heard of it" / "Works on my computer" are dangerous attitudes to have.
- efnx 14y agoI'm sorry, I should have been more clear. I meant "I've never heard of that method, so I looked it up in Xcode and it turns out it is not a method on NSString (and why should it be if !isEqualToString: achieves the same purpose?) which leads me to believe that this comment is sarcasm, if it isn't can you please clarify?"
- seanalltogether 14y agoAlso for what its worth, variable declarations are not nil by default, this bug tied me up for quite some time. NSString *val; if(something){ val = @"hello"; }else if(somthingelse){ val = @"goodbye"; } if(val){ [self display:val]; } this can send you on a wild goose chase for not declaring val = nil initially.
- mikeash 14y agoThis is no longer true when using ARC.
- Scorponok 14y agoThis is no longer true when using ARC. See the "Stack Variables Are Initialized with nil" section of http://developer.apple.com/library/ios/#releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html http://developer.apple.com/library/ios/#releasenotes/Objecti... "Using ARC, strong, weak, and autoreleasing stack variables are now implicitly initialized with nil." This doesn't affect plain C variables, but it would solve the issue you had above.
- micampe 14y agoAnd, if you don’t or can’t use ARC, turn on -Wuninitialized or use the static analyzer (clang warns on uninitialized values only with -Wall).
- xsmasher 14y agoThat's a rule of C; stack vars are not initialized. Same thing would happen with an int. Member variables of a class are set to zero by (init or alloc? can't remember.)
- mpyne 14y agoNice article, but I just wanted to throw out that if being able to call a class method on a null object in C++ is desirable, you can actually check for that condition instead of just crashing. if(!this) return false; // Or whatever Of course it's open for debate as to whether this is something to encourage, but it can help from having to use a Null Object pattern in some situations.
- FigBug 14y agoThis isn't a good idea since even it happens to work it invokes undefined behaviour. If you change the method to virtual it will crash. I think you mean non-virtual method, not 'class method' or static method which wouldn't have a this pointer.
- mpyne 14y agoThat's a good point, calling a virtual method on a null pointer is the source of some backtraces that seem to start a few bytes past 0. Naming things is always difficult, but I meant class method, although what I should have said was non-virtual class method, which is possible in C++. Both virtual and non-virtual have "this" pointers, the difference is whether the class method to call is looked up at runtime or compile-time, so I still would consider both "methods". I didn't learn OOP from Smalltalk so I may be abusing the terminology (it's not intentional).
- mikeash 14y agoI believe the correct terminology for C++ would be "member function", and specifically "non-virtual member function" for the ones that actually work with NULL. I also wouldn't be surprised if using a non-virtual member function with NULL is actually undefined behavior that just happens to work with popular compilers, although I'm too lazy to look up the standard to see. "Class method" is usually used to refer to methods that class objects have themselves, as opposed to an "instance method", which is a method that instances of a class possess. C++ doesn't have class objects or class methods.
- sltkr 14y ago
- j_baker 14y agoIt's worth noting that Go has interesting handling for empty values as well. A nil basically means "the zero value for a given data type". Thus, an empty array compares equal to nil: http://tour.golang.org/#36 http://tour.golang.org/#36
- aaronblohowiak 14y agoThis is consistent with the set-theory perspective of null ( ∅ )
- jerf 14y agoThat is incorrect. Certain types can be nil, and those types have a zero value of nil, but nil is not equal to the zero value for all types. You don't have a "empty array" there, you have an uninitialized slice, with no corresponding array at all, empty or otherwise. Uninitialized slices are nil, but that is not generally true, only for the specified types. If you initialize it, even to an actual "0 array" (as close as we can get), it ceases to be nil. See http://golang.org/ref/spec#The_zero_value http://golang.org/ref/spec#The_zero_value , and w.r.t. the tour code, switch it to: package main import "fmt" func main() { var z []int = make([]int, 0, 0) // jerf changed this line fmt.Println(z, len(z), cap(z)) if z == nil { fmt.Println("nil!") } else { fmt.Println("initialized slice is not nil") } // This won't even compile; "cannot convert nil // to type int" at the if clause: // var a int = 0 // if a == nil { // fmt.Println("jerf is wrong; nil does == 0") // } } (Oh, and for those that don't know, on the other end of that tour link is the live Go tutorial, which allows you to run arbitrary Go code in your browser. You can fiddle with this live, with no installation of Go.)
- scrumper 14y agoAny use cases for Nil, that is, the class pointer to nothing? I've not seen it in the wild (which isn't saying a great deal.)
- mmariani 14y agoYou could use it to test whether or not you really got a class from NSClassFromString(). For instance: Class _myClass; // aClassName was previously defined if((_myClass = NSClassFromString(aClassName)) == Nil) [NSException raise:@"InvalidClass" reason:@"Unknown class named %@.", aClassName); id myObj = [[_myClass alloc] init]; [myObj whatever];
- scrumper 14y agoMakes sense, thanks.
- martinced 14y agoJava is not identical to Obj-C but the 'null' fiasco is mindboggling... Back when the book *"Effective Java" came out, a truly great advice was to use empty arrays everywhere you could instead of null. The second enlightenment came to me when the smart brains at JetBrains decided to ship IntelliJ IDEA with the @NotNull annotation. I basically started to annotate as many methods returning something as @NotNull and then saw the number of NullPointerException go drastically down. It's not possible everywhere but once you start to think about it, you realize that you don't need nearly as many null references as you think you did. Then you started having IntelliJ IDEA warning you real-time (even on incomplete source file) about "reference xxx is never going to be null" (so the non-null check is pointless) or "warning: reference to xxx may be null". Great stuff. Years later there are still 99% of all the Java codebase not using the @NotNull annotation. Sad but true. Now of course other language's take on the subject are interesting too: the maybe monad, the way Clojure deals with empty / nil sequences, etc.
- qu4z-2 14y agoNull is great. Null as a valid value for every reference type by default is brain-dead. It is one of my major gripes with modern C#.
- cjensen 14y agoJust a reminder for C and C++ programmers: NULL considered harmful. Instead, use a literal 0 or (void * ) 0 in portable code. In the C Standard, an implementation can define NULL as either 0 or (void * ) 0. For an example of how that can go terribly wrong, consider the following: printf ("%p\n", NULL) on an architecture where sizeof (int) != sizeof (ptr)
- ksherlock 14y agoc++11 also has nullptr
- cjensen 14y agoWhich will only be portable when all implementations support it :-)
- mpyne 14y agoIt's actually a different answer for C or C++ programmers. NULL should be defined as ((void * )0) for C always (and you should define your own NULL to enforce that if necessary in varargs functions). This is safe because C allows conversion from void * to other pointer types. C++ does not allow conversion from void * to other pointer types, so what you were supposed to do was use plain 0, which introduced exactly the issue you mention (and which has bitten me before trying to use a C library from C++, where NULL was defined as plain 0). With C++11 you should use nullptr, which does the right thing. Some implementations define NULL in C++ to a vendor-provided symbol which emulates nullptr so it might be safe to use NULL. If you have C++98 only and don't want to risk NULL then you should use static_cast<T*>(0) (where T is the actual pointer type), which is admittedly clunky.
- cjensen 14y agoNo! C and C++ are define NULL identically: it can be either 0 or (void * ) 0 at the discretion of the compiler author. So the bug you mention which bit you by using "C library from C++" was not caused by a difference between C and C++. It was caused by a difference in the definition of NULL between those two compilers. You could just as easily have found a C++ compiler which did work, or a C compiler which didn't.