crypt
crypt

Reputation: 449

How is dictionary[@"key"] implemented in objective c?

If we want to override [dictionary objectForKey:@"key"] it can be done by subclassing NSDictionary class. How to override dictionary[@"key"]? In this context I want to know how dictionary[@"key"] is implemented.

Thanks!

Edit: I wanted to find a scalable way to parse an API response while preventing [NSNull null] from crashing my app. I have written category for NSDictionary, but I wanted a way to parse in this syntax: data[@"key"] So, I was evaluating the feasibility of subclassing NSDictionary.

Upvotes: 0

Views: 1561

Answers (3)

Dave Weston
Dave Weston

Reputation: 6635

EDIT: This question is pretty old now, but it just recently came to my attention again, so I thought I would add an example of wrapping NSDictionary as I would recommend. This could be made more robust and feature rich, but it's an okay place to start.

@interface NilSafeDictionary: NSObject
   -(instancetype)initWithDictionary:(NSDictionary*)dictionary;
@end

/** Category on NSDictionary to create a nil-safe wrapper. */
@interface NSDictionary <NilSafeConvenience>
  - (NilSafeDictionary*)nilSafe;
@end

@implementation NilSafeDictionary {
   NSDictionary* _internalDict;
}

- (instancetype)initWithDictionary:(NSDictionary*)dictionary {
  self = [super init];
  if (self) {
    _internalDict = [dict copy];
  }
  return self;
}

- (id)objectForKeyedSubscript:(id<NSCopying>)key {
  id obj = _internalDict[key];
  if ([obj isKindOfClass:[NSNull class]]) {
    return nil;
  }
  return obj;
}

@end

@implementation NSDictionary <NilSafeConvenience>
  - (NilSafeDictionary*)nilSafe {
    return [[NilSafeDictionary alloc] initWithDictionary:self];
  }
@end

CAVEAT: You don't explain here what problem you're trying to solve, but for anyone who comes across this question in the future, subclassing NSDictionary is almost certainly never what you want.

NSDictionary is a class cluster. Class clusters are tricky to subclass correctly with guidance, plus Apple's documentation explicitly tells you not to subclass, so there's no guidance on how to do it correctly.

Even if you're able to use your subclass and have it "work correctly" for your purposes, every other piece of code in the system will still be returning the original NSDictionary class, not your subclass, so it doesn't benefit you.

If you're hell bent on modifying the way NSDictionary works, create your own wrapper class that has an NSDictionary inside and create your own objectForKeyedSubscript: implementation.

Yes, you can add subscript indexing to your own custom class just like NSDictionary!

Upvotes: 1

Leon Storey
Leon Storey

Reputation: 3424

dictionary[@key] is short hand for calling [dictionary objectForKeyedSubscript:@"key"]. Therefore to override the functionality of dictionary[@"key"] you can subclass NSDictionary.

@interface MyDictionary : NSDictionary
  - (id) objectForKeyedSubscript:(id)key;
@end
@implementation MyDictionary
  - (id) objectForKeyedSubscript:(id)key 
  { 
    /*
      ...
    */
  }
@end

Original answer using categories, however this approach isn't advised as it will override the original method removing access to super and potential of clashes should multiple categories be loaded overriding the same method. The last category to be loaded will be used at runtime, therefore you cannot guarantee which that will be.

dictionary[@key] is short hand for calling [dictionary objectForKeyedSubscript:@"key"]. Therefore to override the functionality of dictionary[@"key"] you would create a new category for NSDictionary.

//Category in NSDictionary+CustomKeyedSubscript.h and NSDictionary+CustomKeyedSubscript.m
@interface NSDictionary (CustomKeyedSubscript) 
  - (id) objectForKeyedSubscript:(id)key;
@end
@implementation NSDictionary (CustomKeyedSubscript)
  - (id) objectForKeyedSubscript:(id)key 
  { 
    /*
      ...
    */
  }
@end

Upvotes: 0

mttrb
mttrb

Reputation: 8345

When you use dictionary[@"key"] this gets converted into a call to [dictionary objectForKeyedSubscript:@"key"]. objectForKeyedSubscript: has the same behaviour as objectForKey:.

If you want to change the behaviour of dictionary[@"key"] then you will need to override objectForKeyedSubscript:.

Apple's NSDictionary API Reference has a little more information.

Upvotes: 2

Related Questions