"Mutating method sent to immutable object" despite object being NSMutableDictionary
Asked Answered
S

2

7

I'm using NSMutableDictionary and hit this error:

'NSInternalInconsistencyException', reason: '-[__NSCFDictionary removeObjectForKey:]: mutating method sent to immutable object'

Here's the code:

    // Turn the JSON strings/data into objects
    NSError *error;
    NSMutableDictionary *invoiceDictFromReq = [[NSMutableDictionary alloc] init];
//    invoiceDictFromReq = (NSMutableDictionary *)[NSJSONSerialization JSONObjectWithData:[request responseData] options:kNilOptions error:&error];
    invoiceDictFromReq = [NSMutableDictionary dictionaryWithDictionary:[NSJSONSerialization JSONObjectWithData:[request responseData] options:kNilOptions error:&error]];

NSLog(@"invoiceDictFromReq count: %i, key: %@, value: %@", [invoiceDictFromReq count], [invoiceDictFromReq allKeys], [invoiceDictFromReq allValues]);

// Get values and keys from JSON response
self.invoiceDict = [invoiceDictFromReq objectForKey:@"invoice"];
NSNumber *invoiceAmount = [self.invoiceDict objectForKey:@"amount"];
NSNumber *invoiceId = [self.invoiceDict objectForKey:@"id"];
NSNumber *invoiceNumber = [self.invoiceDict objectForKey:@"number"];
NSNumber *checkoutStarted = [self.invoiceDict objectForKey:@"checkoutStarted"];
NSNumber *checkoutCompleted = [self.invoiceDict objectForKey:@"checkoutCompleted"];
NSLog(@"amount: %@, id: %@, number: %@, started: %@, completed: %@", invoiceAmount, invoiceId, invoiceNumber, checkoutStarted, checkoutCompleted);

All the console logs indicate that the data is fine. This is where things start to break down. I pass the invoiceDict property to the next view controller:

// Pass the invoice to checkoutViewController
[checkoutViewController setInvoiceDict:self.invoiceDict];

In CheckoutViewController.m:

    // Change invoice checkoutCompleted to true
//    [self.invoiceDict removeObjectForKey:@"checkoutCompleted"];
    [self.invoiceDict setObject:[NSNumber numberWithBool:YES] forKey:@"checkoutCompleted"];

The error is at [self.invoiceDict setObject...]. I made sure that all the dictionaries I use are NSMutableDictionary. I left some of the commented-out lines in the code to show the things I've tried and I hit a brick wall. I suppose I can always create a new dictionary. Is that the preferred way to do it?

Singlefoot answered 28/11, 2011 at 16:32 Comment(0)
S
9

You are allocing a dictionary in invoiceDictFromReq and next you are creating another dictionary, you are creating a leak of memory there. Delete the line

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

But your problem is that you are creating a NSMutableDictionary but you are setting to self.invoiceDict a dictionary inside your mutableDictionary, that is not necessarily a mutableDictionary too. Change the line

self.invoiceDict = [invoiceDictFromReq objectForKey:@"invoice"];

for

self.invoiceDict = [NSMutableDictionary dictionaryWithDictionary:[invoiceDictFromReq objectForKey:@"invoice"]];
Shontashoo answered 28/11, 2011 at 16:41 Comment(3)
Thanks for the suggestion but it still gives me the same error. Now, I have invoiceDictFromReq as a property in .h and syntehsized in .m and set it to the data from the json request as self.invoiceDictFromReq = [NSMutableDictionary dictionary...]Singlefoot
Sorry I changed my answer, it was not that. See if you still have the problem. You don't have to retain invoiceDictFromReq because it's a local variable, but delete the line that you create invoiceDictFromReq for the first time because you have a leak thereShontashoo
:-). When I saw, "But your problem is that you are creating a NSMutableDictionary but you are setting to self.invoiceDict a dictionary inside your mutableDictionary, that is not necessarily a mutableDictionary too" I knew you hit it on the spot. So foolish of me. Thanks for pointing it out. I shouldn't have assumed that the native json parser returns NSMutableDictionary. Thanks so much!Singlefoot
C
15

NSJSONSerialization returns immutable objects by default. Here is how to get mutable dictionary from the parser:

  • use option NSJSONReadingMutableContainers

or

  • use mutableCopy on the result
Catalyze answered 11/3, 2013 at 9:57 Comment(1)
You don't need to use mutableCopy, just use the result. NSJSONReadingMutableContainers makes not only arrays and dictionaries within the result mutable, but also the result itself.Humph
S
9

You are allocing a dictionary in invoiceDictFromReq and next you are creating another dictionary, you are creating a leak of memory there. Delete the line

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

But your problem is that you are creating a NSMutableDictionary but you are setting to self.invoiceDict a dictionary inside your mutableDictionary, that is not necessarily a mutableDictionary too. Change the line

self.invoiceDict = [invoiceDictFromReq objectForKey:@"invoice"];

for

self.invoiceDict = [NSMutableDictionary dictionaryWithDictionary:[invoiceDictFromReq objectForKey:@"invoice"]];
Shontashoo answered 28/11, 2011 at 16:41 Comment(3)
Thanks for the suggestion but it still gives me the same error. Now, I have invoiceDictFromReq as a property in .h and syntehsized in .m and set it to the data from the json request as self.invoiceDictFromReq = [NSMutableDictionary dictionary...]Singlefoot
Sorry I changed my answer, it was not that. See if you still have the problem. You don't have to retain invoiceDictFromReq because it's a local variable, but delete the line that you create invoiceDictFromReq for the first time because you have a leak thereShontashoo
:-). When I saw, "But your problem is that you are creating a NSMutableDictionary but you are setting to self.invoiceDict a dictionary inside your mutableDictionary, that is not necessarily a mutableDictionary too" I knew you hit it on the spot. So foolish of me. Thanks for pointing it out. I shouldn't have assumed that the native json parser returns NSMutableDictionary. Thanks so much!Singlefoot

© 2022 - 2024 — McMap. All rights reserved.