Question

I have an NSMutableArray that is holding a list of objects. What I am trying to do is iterate through this list of objects, and find the matching object for the I am trying to insert. Once I find the matching object, I then want to simply replace the object that is currently in the list with the one I am trying to insert. I am trying to do this using fast enumeration:

TestResult *result = [[TestResult alloc] init];
    [result setName:name];
    [result setScore:score];
    [result setDateStamp:date];


    for (TestResult *checkTest in [DataModel sharedInstance].testResultList) {

        NSInteger indx = [[DataModel sharedInstance].testResultList indexOfObjectPassingTest:^BOOL(TestResult *obj, NSUInteger idx, BOOL *stop) {
            return [obj.name isEqualToString:name];
        }];

        if (indx != NSNotFound) {

            [[DataModel sharedInstance].testResultList replaceObjectAtIndex:indx withObject:result];

        }

    }

Unfortunately, when I run the above code, I am getting the following error:

*** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSArrayM: 0x9624820> was mutated while being enumerated.'

Can anyone see what I am doing wrong, and how can work around this, yet achieve the functionality that I described above?

Was it helpful?

Solution

First of all the crash is pretty self explanatory. You are actually mutating (aka. replacing an object in the array), while the fast enumeration is still in progress, which is not permitted.

The solution if you would go with your design would be to actually capture the index of the object, break the fast enumeration and replace the object outside the fast enumeration.

However what you are doing is not correct. The way to use the indexOfObjectPassingTest is this:

NSInteger indx = [[DataModel sharedInstance].testResultList indexOfObjectPassingTest:^BOOL(TestResult *obj, NSUInteger idx, BOOL *stop) {
    return [obj.name isEqualToString:name];
}];

if (indx != NSNotFound) {

    [[DataModel sharedInstance].testResultList replaceObjectAtIndex:indx withObject:result];

}

You don't need to manually enumerate through all elements of the array. The function does this for you internally.

OTHER TIPS

As the error message say you can't modify the array used in a foreach bucle. Change your code to this:

TestResult *result = [[TestResult alloc] init];
[result setName:name];
[result setScore:score];
[result setDateStamp:date];

NSInteger indx = NSNotFound;
for (TestResult *checkTest in [DataModel sharedInstance].testResultList) {

    indx = [[DataModel sharedInstance].testResultList indexOfObjectPassingTest:^BOOL(TestResult *obj, NSUInteger idx, BOOL *stop) {
        return [obj.name isEqualToString:name];
    }];
}
if (indx != NSNotFound) {
    [[DataModel sharedInstance].testResultList replaceObjectAtIndex:indx withObject:result];                            
}

You cannot replace, delete or insert elements in an array while being enumerated. But you could change all the attributes of the element at idxn to the attributes of the result. This way the objects remain.

Hope this helps!

As the error message says, you cannot edit an array which is being enumerated. You need to have a different array and then you can do this.

The following would have worked,

TestResult *result = [[TestResult alloc] init];
[result setName:name];
[result setScore:score];
[result setDateStamp:date];

NSArray *array = [NSArray arrayWithArray:[DataModel sharedInstance].testResultList];    

for (TestResult *checkTest in array) {

    NSInteger indx = [[DataModel sharedInstance].testResultList indexOfObjectPassingTest:^BOOL(TestResult *obj, NSUInteger idx, BOOL *stop) {
        return [obj.name isEqualToString:name];
    }];

    if (indx != NSNotFound) {
        [[DataModel sharedInstance].testResultList replaceObjectAtIndex:indx withObject:result];
    }
}

Update: As per the comment from Lefteris, you dont need to iterate through the array if you are using indexOfObjectPassingTest method.

You can simply use,

TestResult *result = [[TestResult alloc] init];
[result setName:name];
[result setScore:score];
[result setDateStamp:date];

NSInteger indx = [[DataModel sharedInstance].testResultList indexOfObjectPassingTest:^BOOL(TestResult *obj, NSUInteger idx, BOOL *stop) {
    return [obj.name isEqualToString:name];
}];

if (indx != NSNotFound) {
    [[DataModel sharedInstance].testResultList replaceObjectAtIndex:indx withObject:result];
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top