views:

142

answers:

2

I would like to implement an observer pattern in Objective-C where the observer implements an interface similar to SKPaymentTransactionObserver and the observable class just extends my base observable. My observable class looks something like what is below. Notice I'm making copies of the observers before enumeration to avoid throwing an exception .

I've tried adding an NSLock around add observers and notify observers, but I run into a deadlock.

What would be the proper way to handle concurrency when observers are being added as notifications are being sent?

@implementation Observable

-(void)notifyObservers:(SEL)selector {
    @synchronized(self) {
        NSSet* observer_copy = [observers copy];
        for (id observer in observer_copy) {
            if([observer respondsToSelector: selector]) {
                [observer performSelector: selector];
            }
        }
        [observer_copy release];
    }
}

-(void)notifyObservers:(SEL)selector withObject:(id)arg1 withObject:(id)arg2 {
    @synchronized(self) {
 NSSet* observer_copy = [observers copy];
        for (id observer in observer_copy) {
            if([observer respondsToSelector: selector]) {
                [observer performSelector: selector withObject: arg1 withObject: arg2];
            }
        }
       [observer_copy release];
    }
}

-(void)addObserver:(id)observer {
    @synchronized(self) {
        [observers addObject: observer];
    }
}

-(void)removeObserver:(id)observer {
    @synchronized(self) {
       [observers removeObject: observer];
    }
}
+1  A: 

Couldn’t you choose the easier way of notifications?

zoul
There is some discussion here: http://www.cocoadev.com/index.pl?NotificationsAcrossThreads about how to use `NSNotification` instances in multithreaded apps. One entry uses pthreads instead of `NSLock` but the principle should be roughly the same.
Alex Reynolds
Notification center doesn't seem to be a great fit for what I'm trying to do. A lot of the notification callbacks take various parameters and using an observer interface similar to SKPaymentTransactionObserver is much more intuitive.
Steve918
A: 

What’s the problem with the current code? Is it the one that deadlocks? Could you think of the specific deadlock scenario? Did you try to only synchronize the array copy? Like this:

- (void) notify {
    @synchronized(self) {
        NSSet *observersCopy = [observers copy];
    }
    for (id observer in observersCopy)
        [observer doSomething];
    [observersCopy release];
}

- (void) addObserver: (id) observer {
    @synchronized(self) {
        [observers addObject:observer];
    }
}
zoul
The current code actually works ok, but I'm having to copy the array before iterating just to avoid exceptions being raised about enumerating an array while it's being modified. I added a shared NSLock around the notify and add observer methods which is what results in a deadlock. I can post the code that deadlocks.
Steve918
Aha! I think the copy is fine. I would forget it unless it proves to be a bottleneck. I used to copy and allocate arrays even in the main loop of a realtime game.
zoul