Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Defensive measures for accessing arrays and dictionaries

What's the most effective way to defend against nil values in nested dictionaries and arrays?

I have an array that contains dictionaries, which contain arrays, which contain dictionaries, so I have a method that wants to return something like this:

return myArray[0][@"Categories"][0][@"Content"];

Currently I have the following, but I'm wondering if there's a cleaner, or more elegant way to defend these nested structures:

if (myArray) {
  // Add a check for myArray.count > 0
  if ([myArray[0] objectForKey:@"Categories"]) {
    // Add a check for myArray[@"Categories"].count > 0
    if ([myArray[0][@"Categories"][0] objectForKey:@"Content"]) {
      return myArray[0][@"Categories"][0][@"Content"];
    }
  } 
}

Is there a better way to do this? Are there holes I haven't plugged? Thanks in advance!

EDIT: I added comments for adding to check array counts. With those additions is my access to to the object at myArray[0][@"Categories"][0][@"Content"] safe?

like image 662
HackyStack Avatar asked Mar 23 '23 04:03

HackyStack


2 Answers

Neither arrays or dictionaries can contain nil. So you're trying to protect against the requested key / index not existing.

If the dictionary key doesn't exist that's fine, you will just get nil back and any message to nil is 'ignored'.

The problem comes when you have an array and you try to ask for an index that's out of bounds. So what you should really be doing is checking the count of the arrays rather than checking the existence of keys.

like image 99
Wain Avatar answered Mar 25 '23 18:03

Wain


As others have said, neither an NSDictionary nor an NSArray can contain nil values. However, they can contain the (singleton) value NSNull, and an NSDictionary will return nil from objectForKey if the key is not found.

In terms of "defensive" referencing, particularly with decoded JSON, you may need several checks.

First off, make sure you really do have the expected kind of object (some JSON providers may return an outer array on one access and an outer dictionary on the next) by using isKindOfClass: if ([iThinkItsADict isKindOfClass:(NSDictionary class)]) { ...

Next, if accessing an array, make sure the index you are going to use is within the bounds of the array (keeping in mind that an array may have zero entries): if (arrayIndex < someArray.count) { ...

When a value is returned from a dictionary, it may be nil, if the key was not found. So you can test with if (returnedValue != nil) {... (Though note that if a nil value is used to "dispatch" a method call the call will "succeed" without error but return zero/nil. So it is not always necessary to check for nil.)

A dictionary or an array can return an NSNull, which you can check against with if (returnedValue != [NSNull null]) {... (Some will object that you should not use == or != to compare objects for equality, but there is only ever one NSNull object in the app.)

like image 30
Hot Licks Avatar answered Mar 25 '23 18:03

Hot Licks