📜 ⬆️ ⬇️

nil, Nil, NULL, NSNull. All is nothing

The concept of the absence of something — nothing — this is not only a philosophical, but also a completely utilitarian unit: people, like the computers created by them, often have to operate with “empty” ones that express only the non - existence of something, whether it is lack of money on a bank card , about:blank , black holes or grep "${rootpswrd}" /etc/passwd .

To express this “ non-existence ” in programming languages, a large number of different mnemonics are used. We will consider those of them that are used in the superpopular (over the past five years, but we will believe that for long years ahead) the language of Objective-C.


Unification


')
In Objective-C, there are several different types of nothing but the real thing itself . The reason for this is the "multi-layeredness" of the language, which unites both the procedural paradigm of the C language and the object-orientedness of Smalltalk.
C defines nothing as 0 (zero) for most basic ( primitive ) types and as NULL exclusively for pointers.

In fact, in the context of pointers, both NULL and 0 applicable, since the first is nothing more than a macro wrapper for the second:
 #define NULL ((void *)0) 

However, you should not use NULL as a replacement for 0 in those places where zero is an algebraic value:
 int c = b*a; if (c != NULL) { printf("Neither b nor a is equal to 0\n"); } 


Objective-C (like C tuning) retains all the forms of expression of its progenitor, adding one more to them: nil . nil is a pointer to a null object:
 #if !defined(nil) #define nil (id)0 #endif 

That is, technically, it is equivalent to NULL .

Perhaps no Objective-C program can do without the use of the Foundation framework, which in turn also introduces another ( already the fourth in a row ) representation of anything to the language - the NSNull class, which has a single (not counting inherited) method +null which returns a singleton of class NSNull .

But that's not all: in addition to the four above-mentioned mnemonics, the Foundation defines the macro Nil (not to be confused with nil ) - a null pointer of type Class :
 #if !defined(Nil) #define Nil (Class)0 #endif 

An example of using Nil in real life is hard to come up with: only a few geeks deal with classes at runtime . Anyway, here is a small piece of code that initializes a new class inherited from NSString , with the name NSArray (yes, this is not something else, as explained below) in runtime:
 #inlcude <objc/runtime.h> Class foo = objc_allocateClassPair([NSString class], "NSArray", 0); /*      NSArray  ,    Nil —  */ if (Nil != foo) { int check = class_addMethod(foo, @selector(makeWorldBetter), (IMP)_makeWorldBetter, "v@:")); if (check) objc_registerClassPair(foo); } 


Something about nil



Consider a couple of cases when an Objective-C programmer may need nil :
  1. Sometimes the developers of a particular function (method) allow the possibility that not all input parameters can be specified by the user. For example, the -capitalizedStringWithLocale: method of the NSString class takes a locale (an object of the NSLocale class) as an argument, or nil — in the latter case, when changing the case of a string, the canonical decomposition (NFD) of Unicode characters is used regardless of the locale settings on the user's computer (that is, this method will be equivalent to the method -capitalizedString ).
  2. Initialization and “emptying” of objects (for example, in order to report an error):
     - (NSDictionary*)dictionaryForLicenseFile:(NSString *)path { NSData *licenseFile = [NSData dataWithContentsOfFile:path]; /* «,     !» */ if (!licenseFile) return nil; return [self dictionaryForLicenseData:licenseFile]; } 

    It should be noted, by the way, that the practice of ivars initialization common to developers with zero values ​​in the methods of the -init* family -init* not make sense , since their values ​​were already equal to zero even in the -alloc method:
    ... memory for all other instance variables is set to 0.



One of the most remarkable properties of anything (that is, nil ) in Objective-C is the ability to send him absolutely any messages, the answer to which will always be the same nil :
  id foo = nil; int zero = [foo bar: @"Hello, Habr!"]; 

Often this feature is used to reduce the number of checks performed on objects:
 /* In a galaxy far, far away… */ if (foo != nil && [foo testValue: 0x90]) { … } /*         */ if ([foo testValue: 0x310403]) { … } 


Some novice developers, having learned about this feature of nil , transfer it to non-zero objects, forgetting to check whether the called method exists in this class, and inevitably catch exceptions ( exception ) if it is not:
  /* «  NSPersistentStoreCoordinator» © Brad Cox & Tom Love */ NSData *signature = [NSString stringWithFormat: @"%lu", 0xCAFEBABE]; /*   —  @catch  :   -bytes   NSString  */ bytes = [signature bytes]; 


And it would have been okay if only novice developers were doing it ...
A few months ago, I discovered a similar bug in The Tagger’s application license verification system, this bug allowed any user (and not just someone who can work with a file ) to bypass the restriction of the trial version by simply slipping an invalid .lic file (or more precisely .ttl ) to the .ttl .
It goes without saying that I notified the developer about this, but I never received an answer.
By the way, the Cocoa version of the popular class AquaticPrime (which, as a rule, developers prefer the CoreFoundation version) was responsible for the verification code.



In addition, you shouldn’t forget about checking the objects for nil equality: many of your methods from standard frameworks, and (I bet) some of your own, are simply not designed to work with “zero” objects. Take at least +[NSArray arrayWithObjects:] - try to create an array of three objects with nil at the top of the list and look at the result. Another example: +[NSString stringWithString:] , by calling which with nil as an argument, get an exception.

NSNull : nothing or something?



NSNull is used inside the Foundation framework and some others in order to circumvent the limitation of standard collections, such as NSArray or NSDictionary, in that they cannot contain nil values.
NSNull is a kind of wrapper over NULL and nil, allowing you to store them in Objective-C collection objects.


It may not be entirely clear why you need to store zero objects somewhere. Consider an example: in your iOS application, you need to parse some JSON file with a piece like this
 { "keys": ["X"], "command": "set_action_motion", "args": { "action": "vi_left_delete", "motion": null }, "context": [{"key": "setting.command_mode", "modes": [] }] } 

Most likely, you will not parse JSON "hands", but take advantage of a ready-made library (like JSONKit ), which at the time converts it into objects of standard Objective-C classes. However, the value of args["motion"] is nothing , that is, null ! This value will be read into an object of the NSNull class as a result of the parser operation.

Again 11001b



Incomplete summary table:

DesignationFlick of the wrist ...Poslenie
00Zero - he is zero everywhere
Null(void *) 0C pointer zero
nil(id) 0Null pointer to an Objective-C object
Nil(Class) 0Zero pointer of type Class in Objective-C
NSNull[NSNull null]Singleton class NSNull - wrappers over nil and Null


Literature




PS I did not design this topic as a translation, because I pretty much eventually moved away from the original article. Hopefully for the better.

Source: https://habr.com/ru/post/165021/


All Articles