nil, Nil, NULL, NSNull. All is nothing

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

To express such " nonexistence " in programming languages, a large number of different mnemonics are used. We will consider those of them that are used in the super-popular (over the past five years, but, we believe that for the long summer ahead) language Objective-C.


Unification



Objective-C has several different types of real nothing . The reason for this is the “multilayered” language, combining both the procedural paradigm of the C language and the object-oriented Smalltalk.
C defines nothing as 0(zero) for most basic ( primitive ) types and as NULLexclusively for pointers.

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

However, it should not be used NULLas a replacement 0in 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 (as setup in C) has retained all forms of expression nothing of his ancestor, adding to them another: nil. nilthis is a pointer to a null object:
#if !defined(nil)
    #define nil (id)0
#endif

That is, technically, it is equivalent NULL.

Perhaps no Objective-C program can do without using the Foundation framework, which in turn also introduces another ( already the fourth in a row ) representation of nothing into the language - a class NSNullthat has a single (apart from the inherited) method +nullthat returns singleton class NSNull.

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

It’s Nilhard to come up with a real-life example : only a few geeks are involved in manipulating classes in runtime . Anyway, here is a small piece of code that initializes a new class inherited from , with the name (yes, this is not a nonsense, explanation below) in runtime:NSStringNSArray
#inlcude 
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 where an Objective-C programmer might need nil:
  1. Sometimes the developers of a particular function (method) admit the possibility that not all input parameters can be set by the user. For example, a -capitalizedStringWithLocale:class method NSStringtakes a locale (an object of the class NSLocale) as an argument , or nil- in the latter case, when changing the case of a string, the canonical decomposition (NFD) of Unicode characters will be 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];
    }  	
    

    By the way, it should be noted that the practice of initializing ivars with zero values ​​in family methods , which is widespread among developers, -init*does not make sense , since their values ​​were already set to zero in the method -alloc:
    ... memory for all other instance variables is set to 0.



One of the most remarkable features of anything (that is, nil) in Objective-C is the ability to send it any kind of message, the answer to which will invariably 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: 0x31с040с3]) { … } 


Some novice developers to learn about this feature nil, it is transferred and nonzero objects, forgetting to check whether the called method exists in this class, and inevitably catch exceptions ( by exception ), if it is not:
   /* 
      «Да хоть NSPersistentStoreCoordinator» ©  Brad Cox & Tom Love
  */
  NSData *signature = [NSString stringWithFormat: @"%lu", 0xCAFEBABE];
  /*
      Исключение вылетит —  без @catch не поймаешь: никакого метода -bytes у класса NSString нет
   */
  bytes = [signature bytes];


And it’s ok, only novice developers would hunt for 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 those who can work with a file ) to circumvent the trial version limitation by simply slipping an invalid file .lic(more precisely .ttl) into the program .
Needless to say, I notified the developer about this, but never received a response.
By the way, the Cocoa version of the popular AquaticPrime class (which, usually, developers prefer the CoreFoundation version) was responsible for the verification code .



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

NSNull: nothing or something?



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


It may not be completely clear why you need to store zero objects somewhere. Let's look at an example: in your iOS application, you need to parse a certain JSON file in which there is 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 “by hand”, but rather use a ready-made library (like JSONKit ), which at the moment converts it into objects of standard Objective-C classes. However, the meaning args["motion"]is equal to nothing , that is null! This value will be read into the class object NSNullas a result of the parser.

Again 11001b



Non-summary pivot table:

DesignationWith a flick of the wrist ...Revelation
00Zero - it's everywhere zero
Null(void *) 0C null pointer
nil(id) 0Objective-C null pointer
Nil(Class) 0Class null pointer in Objective-C
NSNull[NSNull null]Singleton NSNull - wrappers over nil and Null


Literature




PS I did not begin to design this topic as a first-source, because as a result, I pretty much moved away from the original article. I hope for the better.

Also popular now: