8

I had a problem in my project where I declared in the header file an NSMutableDictionary property like so:

@property (copy, nonatomic) NSMutableDictionary *DataDict ;

Now in the implementation file, I go ahead and initialise this dictionary because I am gonna use it, like so:

DataDict = [[NSMutableDictionary alloc]init];

Now when I did this, the minute I try to add something to this dictionary I would get this error message:

-[__NSDictionaryI setObject:forKey:]: unrecognized selector sent to instance 0x885ae60 2012-10-19 16:51:56.040 testing[2297:c07] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSDictionaryI setObject:forKey:]: unrecognized selector sent to instance 0x885ae60'

After a while and running through my project a thousand times, I decided to uncomment my initialization line, like so

 //DataDict = [[NSMutableDictionary alloc]init];

and that fixed the problem.

My question is: Why?

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
Kalamantina
  • 303
  • 4
  • 10

3 Answers3

15

The problem is in the way you have defined your property. If you change it to:

@property (strong, nonatomic) NSMutableDictionary *DataDict ;

instead of copy everything should be fine.

This happens because you basically say that you want a copy of your object through your generated accessors, which returns an NSDictionary instead (an immutable copy).

You can find more on objective-c properties here.

Just as a sidenote: objective-c ivars usually start with a lowercase letter (uppercase names are used for classes), so dataDict should be preferred over DataDict.

Jason
  • 2,223
  • 2
  • 20
  • 28
Alladinian
  • 34,483
  • 6
  • 89
  • 91
  • Does that mean I should make the reference strong, and then alloc init? or make it strong and no need to alloc init? – Kalamantina Oct 19 '12 at 21:22
  • By making it `strong` you just saying that you want to keep a strong relationship (owning) with that object (which basically means that you want it to be deallocated when the owner deallocates). You still have to alloc/init the object as always. – Alladinian Oct 19 '12 at 21:39
2

It is because the property have "copy" attribute so NSMutableDictionary instance alloc/init-ed is "copy"'ed using "copy" method, and "copy" method create not NSMutableDictionary but NSDictionary. ("mutableCopy" will create NSMutableDictionary).

Probably, you can use "retain" instead of "copy" as attributes.

@property (retain, nonatomic) NSMutableDictionary *DataDict ;

Or, just without "copy"/"retain" but use ARC.(Automatic reference counting).

Tsuneo Yoshioka
  • 7,504
  • 4
  • 36
  • 32
0

I have this exact problem. No combination of retain/copy/strong/weak, etc do the trick. What does work is to create a temporary Mutable Dictionary, load it up and then set my original equal to it.

 NSMutableDictionary * tempD = [[NSMutableDictionary alloc] init];

[tempD setObject: epsTapeCut forKey:LWPrintParameterKeyTapeCut];
[tempD setObject: epsCopies forKey:LWPrintParameterKeyCopies];
[tempD setObject: epsHalfCut forKey:LWPrintParameterKeyHalfCut];
[tempD setObject: epsPrintSpeed forKey:LWPrintParameterKeyPrintSpeed];
[tempD setObject: epsDensity forKey:LWPrintParameterKeyTapeWidth];

self.ePSprintSettings = tempD;

This fails:

 [self.ePSprintSettings setObject: epsTapeCut forKey:LWPrintParameterKeyTapeCut];

Declaration:

 @property (nonatomic, retain) NSMutableDictionary *ePSprintSettings;

(But again, no combination of attributes makes a difference.)

Initialization:

 self.ePSprintSettings = (NSMutableDictionary *)[myUserDefaults dictionaryForKey:kEpsPrintSettings];

Thank you for helping me understand.

I'm happy enough that this works, but I'd like to understand why.

user938797
  • 167
  • 3
  • 15