48

This might be very basic question but I was wondering why can't I assign nil as NSDictionary value? I have following statement many places in my code. If [q objectForKey:@"text"] is nil then App is crashing.

NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithCapacity:2];
[dict setObject:[q objectForKey:@"text"] forKey:@"text"];

I have to check everywhere for the nil before assigning it to dictionary. Is this the only correct way of doing? Am I missing something obvious?

if([q objectForKey:@"text"] != nil)
    [dict setObject:[q objectForKey:@"text"] forKey:@"text"];
else
    [dict setObject:@"" forKey:@"text"];
David Rönnqvist
  • 56,267
  • 18
  • 167
  • 205
Paresh Masani
  • 7,474
  • 12
  • 73
  • 139

5 Answers5

73

It wants an actual object... use NSNull

[NSNull null];
Albert Renshaw
  • 17,282
  • 18
  • 107
  • 195
tybro0103
  • 48,327
  • 33
  • 144
  • 170
  • 1
    This looks great. When I set NSNull, the condition if([q objectForKey:@"text"] == nil) will be true or do I have to use if([q objectForKey:@"text"] == NSNull)? Thanks. – Paresh Masani Aug 17 '12 at 15:16
  • 3
    @AppleDeveloper it won't be true, you will have to check against `[NSNull null]` – Peter Pajchl Aug 17 '12 at 15:21
  • Thanks @peter so I will need to use two different conditions? if([q objectForKey:@"text"] == nil) to check if key is exists or not and if([q objectForKey:@"text"] == [NSNull null]) to check if key has null value? – Paresh Masani Aug 17 '12 at 15:24
  • 3
    @AppleDeveloper since you can't store `nil` in `NSDictionary`, checking for it is obsolete as it can't ever happen. So, no, don't check for `nil`, only check for `[NSNull null]`. – Peter Pajchl Aug 18 '12 at 11:31
  • @PeterPajchl it will be nil if the key is doesn't have a value. – Abhi Beckert Jan 13 '21 at 01:09
51

You can set a nil value using setValue:forKey but it removes the key.

If you want to be able to set a key to nil you could use setValue:forKey: which will remove the key if you set it to nil (quote from documentation below). Note the Value instead of Object.

setValue:forKey:

Adds a given key-value pair to the dictionary.

...
Discussion

This method adds value and key to the dictionary using setObject:forKey:, unless value is nil in which case the method instead attempts to remove key using removeObjectForKey:.

When you later try and get the object using objectForKey: for the key that you removed by setting it to nil you will get nil back (quote from documentation below).

Return value:

The value associated with aKey, or nil if no value is associated with aKey.

Note: The key will not actually be present in the dictionary so it won't be obtained using allKeys; or be enumerated over.

David Rönnqvist
  • 56,267
  • 18
  • 167
  • 205
  • 3
    Hi David, Thanks for the useful info but I read on Apple documents that you use setValue only for KVO and for normal key-value pair, you should always use setObject! Isn't that true? – Paresh Masani Aug 17 '12 at 15:21
  • 4
    @AppleDeveloper As far as I know, dictionaries have their own setValue implementation. The NSMutableDictionary documentation (linked in my answer) says nothing about not using it: "`setValue:forKey:` Adds a given key-value pair to the dictionary. ... **Discussion** This method adds value and key to the dictionary using `setObject:forKey:`, unless value is nil in which case the method instead attempts to remove key using `removeObjectForKey:`." – David Rönnqvist Aug 17 '12 at 15:25
  • 1
    A significant restriction of this approach is that `setValue:withKey:` expects a string key. It's not entirely clear that using other types is safe, and the compiler issues a warning for it. I think I'd avoid it, personally. I think a category on `NSMutableDictionary` providing `setOrClearObject:forKey:` would be a safer approach. – Benjohn Jun 23 '15 at 20:59
5

You can set nil object in this way:

NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];

dictionary[@“key”] = nil;

Have you noticed it?

NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];

/* this statement is safe to execute    */

dictionary[@“key”] = nil;

/* but this statement will crash application    */

[dictionary setObject:nil forKey:@"key"];
SamSol
  • 2,885
  • 6
  • 37
  • 56
1

When using this method:

func setObject(_ anObject: Any, forKey aKey: NSCopying)

Parameters (according to Apple doc's):

anObject:

Raises an invalidArgumentException if anObject is nil. If you need to represent a nil value in the dictionary, use NSNull .

aKey

Raises an invalidArgumentException if aKey is nil.

Avi Levin
  • 1,868
  • 23
  • 32
0

My friend using nil as marker is a sign of bad programming . nil is reserved for some diffrent purpose .

if([q objectForKey:@"text"] != nil)
    [dict setObject:[q objectForKey:@"text"] forKey:@"text"];
else
    [dict removeObjectforKey:@"text"]; // this will do nothing if key does not exsist.

//by default for all the keys the value is nil and you are trying to override this behavior. going against the language rules will always get you in trouble .

to check just use

if([dict objectforKey:@"text"] !=nil){} // this will work becuase default value is nil 
itself 
Kunal Balani
  • 4,739
  • 4
  • 36
  • 73
  • I know that but a I had to do this to set default value for the key! Can you just create key without assigning value? – Paresh Masani Aug 17 '12 at 15:18
  • @AppleDeveloper Please explain what you would gain by having a key without a value... – David Rönnqvist Aug 17 '12 at 15:19
  • 3
    The short ans is no and long ans is Noooooooooooooo. – Kunal Balani Aug 17 '12 at 15:20
  • @DavidRönnqvist sorry I know it's not possible but I was little sarcastic there! I am building 100s of key-value pair where some of them have values and some are missing and will be filled by user. I am generating dynamic UI controls so still if particular key doesn't have an answer/default value,I need to store that key for future reference and assign the value later on. – Paresh Masani Aug 17 '12 at 15:27
  • @AppleDeveloper we all hinting you that you have a wrong design . Insert key value when you have both (this will save you memory too) . I dont know what your context is but you can always have a better solution than inserting nil value . – Kunal Balani Aug 17 '12 at 15:31
  • @KunalBalani I generate UI dynamically, each key has, particular control associated with it. When control value changes, I need to set the value to associated key and not any other so I have to preserve it and attach with the control. I know I am doing it correct but anyway I would check if I can avoid using nil. Thanks. – Paresh Masani Aug 17 '12 at 15:35
  • ....and just for your information I haven't been assigning nil but @"" and it's the way whole ASIHTTPRequest framework designed and implemented. – Paresh Masani Aug 17 '12 at 15:50
  • I would like to support AppleDeveloper here. Storing nil in a dictionary is a perfectly sensible thing to do in many situations (like the one AppleDeveloper mentions), and not necessarily a wrong design. And there is no reason at all for Apple not to support it. – fishinear Oct 24 '12 at 14:24
  • I disagree about the assumption that wanting to store nil implies bad programming. In many scenarios, there is an important difference between knowing that a key is nil and knowing nothing at all. Why not be able to model this case? Broadly speaking, broad statements are hardly ever true ;) – eremzeit Jul 17 '14 at 22:17
  • @eremzeit I agree that broad statements are not true. How about you come up with an example which requires to store nil and I will tell you a way to solve the same problem without it. – Kunal Balani Jul 18 '14 at 14:46
  • Thats why Apple developed Swift. A collection or array can contain values of a specific type. Since swift arrays do not care about the specific type, the values can be of a optional type. For example your swift array can contain values of type String? (optional String). For every index, there can be value of type String or no value at all (nil) which basically means the value is not available in Swift. – user3378170 Mar 26 '15 at 15:40
  • `objectForKey:` returns object. So you need a voting down because you don't simply write `![q objectForKey:@"text"]` – Vyachaslav Gerchicov Jul 21 '15 at 08:45