"Mutating method sent to immutable object" despite object being NSMutableDictionary

12,654

Solution 1

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"]];

Solution 2

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
Share:
12,654
oky_sabeni
Author by

oky_sabeni

Updated on June 04, 2022

Comments

  • oky_sabeni
    oky_sabeni almost 2 years

    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?

  • oky_sabeni
    oky_sabeni over 12 years
    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...]
  • Bruno Domingues
    Bruno Domingues over 12 years
    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 there
  • oky_sabeni
    oky_sabeni over 12 years
    :-). 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!
  • gnasher729
    gnasher729 almost 10 years
    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.