Search code examples
iosobjective-cnsdictionaryobjective-c-category

Inconsistency in Category extensions iOS 10+


I have a very strange issue that I cannot seem to see the cause of.

I have a category extension on NSDictionary that has a method called title. The method works as follows:

- (NSString*)title {
    return self[@"title"];
}

What this allows me to do, is have a conveniece method on a NSDictionary to return a title.

Here is an example of use:

NSDictionary *config = [mConfigManager currentConfig];

if ([config title]) {

    return [config title];
}
return @"";

Now this is where things get strange. On iOS 10 the title method sometimes returns nil. In cases where this returns nil, a manual call to config[@"title"] returns the value. As a result I have adjusted my code as follows:

NSDictionary *config = [mConfigManager currentConfig];

if ([config title]) {

    return [config title];
}
else if(config[@"title"])
{
    return config[@"title"];
}
return @"";

With the above code, this always returns the title (if it exists). However it sometimes only works for the else if case and not the category version.

So the question is:

Why would the category not return a value when it is doing exactly the same thing as the manual call?

What makes this even stranger is that is does sometime work: See the following log output:

first click

(lldb) po [config title]
Joe Bloggs

second click

(lldb) po [config title]
nil
(lldb) po config[@"title"]
Joe Bloggs

Note: The above logs both ran during the same instance of the app, triggered by selecting a table view cell. So this eliminates any cases where a deallocation or re-allocation could occur.

One thing I have noticed however, is that the console will not output the contents of config and instead returns the following error:

<object returned empty description>

The variable view however fully shows the object and all variables. The object is defined as NSDictionaryM in the variable list.

Any help with this would be massively appreciated.

Thanks in advance


Solution

  • Very likely something else in the system, possibly (probably) from Apple, has also created a title method on NSDictionary in a category. When there are two methods with the same name defined in categories, it is undefined which one will be executed. (I personally encountered this when Apple added pop to NSMutableArray in a private category, and my program went haywire in unpredictable ways.)

    You must prefix your category methods (i.e. mc_title or whatever). Apple should prefix their private methods, but they don't, so you must.

    (Of course, wanting to add this method to NSDictionary strongly suggests that you really should create a small data class instead of using a dictionary for this, but that's a completely separate issue.)