2

I have a situation where I want to map a pair of objects to a dictionary of information. I'd like to avoid creating an NSDictionary of NSDictionaries of NSDictionaries since that's simply confusing and difficult to maintain.

For instance if I have two classes, Foo and Bar, I want {Foo, Bar} -> {NSDictionary}

Are there other options than just making a custom class (just to be used as the dictionary key) based on the two types in question? Maybe something akin to STL's pair type that I just don't know about?

nall
  • 15,899
  • 4
  • 61
  • 65

3 Answers3

5

Like you said, you can create a Pair class:

//Pair.h
@interface Pair : NSOpject <NSCopying> {
  id<NSCopying> left;
  id<NSCopying> right;
}
@property (nonatomic, readonly) id<NSCopying> left;
@property (nonatomic, readonly) id<NSCopying> right;
+ (id) pairWithLeft:(id<NSCopying>)l right:(id<NSCopying>)r;
- (id) initWithLeft:(id<NSCopying>)l right:(id<NSCopying>)r;
@end

//Pair.m
#import "Pair.h"
@implementation Pair
@synthesize left, right;

+ (id) pairWithLeft:(id<NSCopying>)l right:(id<NSCopying>)r {
 return [[[[self class] alloc] initWithLeft:l right:r] autorelease];
}

- (id) initWithLeft:(id<NSCopying>)l right:(id<NSCopying>)r {
  if (self = [super init]) {
    left = [l copy];
    right = [r copy];
  }
  return self;
}

- (void) finalize {
  [left release], left = nil;
  [right release], right = nil;
  [super finalize];
}

- (void) dealloc {
  [left release], left = nil;
  [right release], right = nil;
  [super dealloc];
}

- (id) copyWithZone:(NSZone *)zone {
  Pair * copy = [[[self class] alloc] initWithLeft:[self left] right:[self right]];
  return copy;
}

- (BOOL) isEqual:(id)other {
  if ([other isKindOfClass:[Pair class]] == NO) { return NO; }
  return ([[self left] isEqual:[other left]] && [[self right] isEqual:[other right]]);
}

- (NSUInteger) hash {
  //perhaps not "hashish" enough, but probably good enough
  return [[self left] hash] + [[self right] hash];
}

@end

Edit:

Some notes on creating objects that can be keys:

  1. They have to conform to the NSCopying protocol, since we don't want the key to change out from underneath us.
  2. Since the Pair itself is copied, I make sure the objects in the pair are also copied.
  3. Keys have to implement isEqual:. I implement the hash method for good measure, but it's probably not necessary.
Dave DeLong
  • 242,470
  • 58
  • 448
  • 498
  • Dave, 2 questions. 1) I want allocWithZone in copyWithZone, right? 2) Is there a benefit to using id instead of NSObject*? – nall Oct 20 '09 at 06:16
  • 3
    If you implement isEqual: you absolutely have to implement -hash. The invariant is that if [a isEqual:b], then [a hash] must == [b hash]. – Ken Oct 20 '09 at 07:01
  • @nall (sorry, missed your comment) 1: `+alloc` is implemented by calling `+allocWithZone:`, so it probably shouldn't make a difference. 2: There's no real benefit. I prefer `id` because it's a) less to type and b) initializers return `id`, not `NSObject *` (or similar variant). True, you will *rarely* have an initializer that doesn't return some `NSObject` subclass, but it's not impossible. `NSObject` is not the only root class. – Dave DeLong Nov 01 '09 at 04:40
  • 1
    You can cut that `finalize` method. `release` is a no-op in GC, and you don't need a `finalize` method just to set ivars to `nil`; those references will die anyway after GC reaps your object. – Peter Hosey Mar 02 '10 at 10:31
3

You could try NSMapTable, then your key can be pretty much anything, perhaps a pointer to a structure if you want.

(Addendum to the comments below: NSMapTable was once not on iOS, but is available as of iOS 6.)

Nicholas Riley
  • 43,532
  • 6
  • 101
  • 124
  • That's a nice hint, but NSMapTable is not available in iOS (currently 5.0.1). – auco Dec 28 '11 at 17:50
  • That makes some sense, as NSMapTable was primarily developed as an adjunct to Objective-C garbage collection (although it's quite useful in non-GC environments). There's always CFMutableDictionary if you want something that behaves like NS(Mutable)Dictionary but supports non-object types. – Nicholas Riley Dec 28 '11 at 18:37
  • more info on NSMapTable: http://stackoverflow.com/questions/6904533/nsmaptable-and-nsmutabledictionary-differences/12186329#12186329 – Steph Thirion Aug 29 '12 at 20:56
0

Is using something to the effect of

[NSString stringWithFormat:@"%@ %@", [obj1 key_as_string], [oj2 key_as_string]]

no good for you, where key_as_string is part of your object class? i.e. make a key from a string.

Ken
  • 30,811
  • 34
  • 116
  • 155