1

My problem is that I have a class property that is of type NSMutableArray, as defined in my header file, yet when I attempt to modify one of the array elements (an NSDictionary) I receive the following runtime error:

2013-01-16 14:17:20.993 debtaculous[5674:c07] * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFArray replaceObjectAtIndex:withObject:]: mutating method sent to immutable object'

Header declaration:

//  BudgetViewController.h

#import <UIKit/UIKit.h>

@interface BudgetViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
- (IBAction)afterTaxIncomeEditingDidEnd:(id)sender;
@property (strong, nonatomic) NSMutableArray *budgetArray;
@property (strong, nonatomic) IBOutlet UITextField *afterTaxIncome;
@property (strong, nonatomic) IBOutlet UITableView *budgetTableView;

@end

Method that generates the error:

-(void)applyCCCSWeights
{
    NSMutableDictionary *valueDict;
    NSString *newAmount;

    for (id budgetElement in [self budgetArray]) {
        valueDict = [[NSMutableDictionary alloc] initWithDictionary:budgetElement];
        newAmount = [NSString stringWithFormat:@"%0.2f", [[self afterTaxIncome].text floatValue] * [[budgetElement objectForKey:@"cccs_weight"] floatValue]];
        [valueDict setValue:newAmount forKeyPath:@"amount"];

        [[self budgetArray] replaceObjectAtIndex:0 withObject:valueDict];
        NSLog(@"%0.2f (%0.2f)", [[budgetElement objectForKey:@"amount"] floatValue], [[self afterTaxIncome].text floatValue] * [[budgetElement objectForKey:@"cccs_weight"] floatValue]);
    }

    [self.budgetTableView reloadData];
}

// Note the replaceObjectAtIndex:0 above is just a placeholder. This will be replaced with the correct index.

4
  • Where do you init your array? Commented Jan 16, 2013 at 19:32
  • Can you post the code where you initialize budgetArray Commented Jan 16, 2013 at 19:32
  • Main parts: [self setUrlData:[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]]; [self setBudgetArray:[NSJSONSerialization JSONObjectWithData:[self urlData] options:kNilOptions error:&error]]; Commented Jan 16, 2013 at 20:17
  • There you go... Look at the options in NSJSONSerialization. Use one that creates mutable arrays and dictionaries. Commented Mar 17, 2014 at 0:05

4 Answers 4

4

budgetArray is surely immutable, you have to create it mutable.

Probably you're doing something like this:

budgetArray= [NSArray arraWithObjects; obj1, obj2, nil];

And ignoring the compiler warning. Make it mutable:

budgetArray= [[NSMutableArray alloc]init];
Sign up to request clarification or add additional context in comments.

1 Comment

I found a related Question that may be the root of the issue: stackoverflow.com/questions/9912707/…
3

I'm fairly certain you cannot change a mutable object during enumeration.

This SO question may help: Setting an object during fast enumeration problems

1 Comment

You're right but that isn't the exception he is receiving. So +1 for predicting his next SO question after he listens to @Ramy
0

In your init method, put this:

budgetArray = [[NSMutableArray alloc] init];

Also, why not use dictionary and array literal syntax?

-(void)applyCCCSWeights {
    NSMutableDictionary *valueDict;
    NSString *newAmount;

    for (NSDictionary *budgetElement in [self budgetArray]) {
        valueDict = [budgetElement mutableCopy];
        newAmount = [NSString stringWithFormat:@"%0.2f", [[self afterTaxIncome].text floatValue] * [budgetElement[@"cccs_weight"] floatValue]];
        valueDict[@"amount"] = newAmount;

        _budgetArray[0] = valueDict;
        NSLog(@"%0.2f (%0.2f)", [budgetElement[@"amount"] floatValue], [[self afterTaxIncome].text floatValue] * [budgetElement[@"cccs_weight"] floatValue]);
    }

    [self.budgetTableView reloadData];
}

Notice that [[self budgetArray] replaceObjectAtIndex:0 withObject:valueDict];

becomes: _budgetArray[0] = valueDict;

2 Comments

Thanks all for the help. My final solution was to use the above method and a local "NSMutableArray *tempArray" to hold the new values and finally: self.budgetArray = [[NSMutableArray alloc] initWithArray:tempArray]; [self.budgetTableView reloadData];
See comment above. Read the NSJSONSerialization documentation.
0

You can't change an array while doing a fast iteration over the array. On the other hand, it's entirely unnecessary; the code is absolutely inefficient: Just make the elements of the array NSMutableDictionaries, then change the dictionaries directly, instead of creating a copy and then changing an element in the copy.

Noticed later that you use NSJSONSerialization; look at the flags and don't pass 0 blindly.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.