Search code examples
iphoneobjective-ciosxcodeenumerate

Error when using NSMutableSet


I get the error

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

when adding an new delegate to my class. Or at least, that's where I think the problem is.

This is my code: MyAppAPI.m

[...]
static NSMutableSet *_delegates = nil;

@implementation MyAppAPI

+ (void)initialize
{
    if (self == [MyAppAPI class]) {
        _delegates = [[NSMutableSet alloc] init];
    }
}

+ (void)addDelegate:(id)delegate
{
    [_delegates addObject:delegate];
}

+ (void)removeDelegate:(id)delegate
{
    [_delegates removeObject:delegate];
}
[...]

@end

MyAppAPI is a singleton which I can use throughout my application. Wherever I can (or should be able to) do: [MyAppAPI addDelegate:self].
This works great, but only in the first view. This view has a UIScrollView with PageViewController which loads new views within itself. These new views register to MyAppAPI to listen to messages until they are unloaded (which in that case they do a removeDelegate). However, it seems to me that it dies directly after I did a addDelegate on the second view in the UIScrollView.

How could I improve the code so that this doesn't happen?

Update
I'd like to clarify me a bit further. What happens is that view controller "StartPage" has an UIScrollView with a page controller. It loads several other views (1 ahead of the current visible screen). Each view is an instans PageViewController, which registers itself using the addDelegate function shown above to the global singleton called MyAppAPI. However, as I understand this viewcontroller 1 is still reading from the delegate when viewcontroller 2 registers itself, hence the error shows above.

I hope I made the scenario clear. I have tried a few things but nothing helps. I need to register to the delegate using addDelegate even while reading from the delegates. How do I do that?

Update 2 This is one of the reponder methods:

+ (void)didRecieveFeaturedItems:(NSArray*)items
{   
    for (id delegate in _delegates)
    {
        if ([delegate respondsToSelector:@selector(didRecieveFeaturedItems:)])
            [delegate didRecieveFeaturedItems:items];
    }
}

Solution

  • Scott Hunter is right. This error is thrown when you try to edit a list while iterating.

    So here is an example of what you may be doing.

    + (void)iteratingToRemove:(NSArray*)items {   
        for (id delegate in _delegates) {
            if(delegate.removeMePlease) {
              [MyAppAPI removeDelegate:delegate];  //error you are editing an NSSet while enumerating
            }
        }
    }
    

    And here is how you should handle this correctly:

    + (void)iteratingToRemove:(NSArray*)items
    {   
        NSMutableArray *delegatesToRemove = [[NSMutableArray alloc] init];
        for (id delegate in _delegates) {
            if(delegate.removeMePlease) {
              [delegatesToRemove addObject:delegate];
            }
        }
    
        for(id delegate in delegatesToRemove) {
             [MyAppAPI removeDelegate:delegate];  //This works better
        }
    
        [delegatesToRemove release];
    }