/** EOObserver.m
This is the central coordinating class of the change tracking system * of EOControl. It manages the observers [(EOObserving)] and the objects * to be observed. No instances of this class should be needed or created. * All methods for coordinating the tracking mechanism are class methods.
*Observers must implement the [(EOObserving)] protocol, in particular * [(EOObserving)-objectWillChange:] while the observed objects must invoke * [NSObject-willChange]. Invoke [+addObserver:forObject:] to register an * observer for a particular object. That will setup for * [(EOObserving)-objectWillChange:] to be invoked on the observer each time * [NSObject-willChange] gets called. To remove an observer invoke * [+removeObserver:forObject:]. For observers who wish to be notified for * every change the methods [+addOmniscientObserver:] and * [+removeOmniscientObserver:] can be used.
*/ @implementation EOObserverCenter static NSMapTable *observersMap = NULL; static NSHashTable *omniscientHash = NULL; static unsigned int notificationSuppressCount=0; static id lastObject; + (void)initialize { observersMap = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks, NSNonOwnedPointerMapValueCallBacks, 32); omniscientHash = NSCreateHashTable(NSNonOwnedPointerHashCallBacks, 0); lastObject = nil; notificationSuppressCount = 0; } /** ** Adds the observer to be notified with a [(EOObserving)-objectWillChange:] * on the first of consecutive [NSObject-willChange] methods invoked on * the object. *
** This does not retain the object. It is the observers * responsibility to unregister the object with [+removeObserver:forObject:] * before the object ceases to exist. * The observer is also not retained. It is the observers responsibility * to unregister before it ceases to exist. *
*
* Both observer and object equality are considered equal through pointer
* equality, so an observer observing multiple objects considered
* isEqual:
* will recieve multiple observer notifications,
* objects considered isEqual:
may contain different sets of
* observers, and an object can have multiple observers considered
* isEqual:
.
*
NSRunLoop currentRunLoop
with
* EOFlushDelayedObserverRunLoopOrdering
* to invoke [-notifyObserversUpToPriority:] with EOObserverPrioritySixth.
* In general this mechanism is used by [EOAssociation] subclasses and
* in part [EODisplayGroup] or even [EOEditingContext].
*/
@implementation EODelayedObserverQueue
static EODelayedObserverQueue *observerQueue;
/**
* Returns the default queue.
*/
+ (EODelayedObserverQueue *)defaultObserverQueue
{
if (!observerQueue)
observerQueue = [[self alloc] init];
return observerQueue;
}
/**
* Initializes the an EODelayedObserverQueue
* with NSDefaultRunLoopMode.
*/
- init
{
if (self == [super init])
{
ASSIGN(_modes, [NSArray arrayWithObject: NSDefaultRunLoopMode]);
}
return self;
}
- (void)_notifyObservers: (id)ignore
{
[self notifyObserversUpToPriority: EOObserverPrioritySixth];
_haveEntryInNotificationQueue = NO;
}
/**
* Registers the observer with the receiver so that it will * dispatch [EODelayedObserver-subjectChanged] either immediately, * if the observers priority is EOObserverPriorityImmediate, or * for the next invocation of [-notifyObserversUpToPriority:]. If * this method is invoked during the processing of * [-notifyObserversUpToPriority:], then the dispatch will be enqueue * for the current processing.
*Upon the first invocation within a run loop, this method registers
* a callback method to have [-notifyObserversUpToPriority:] invoked
* with EOObserverPrioritySixth with the
* NSRunLoop currentRunLoop
with
* EOFlushDelayedObserverRunLoopOrdering and the receivers run loop modes.
The observer is not retained and should be removed from the receiver
* with [-dequeueObserver:] during the EODelayedObservers
* NSObject dealloc
method.
Dispatches registered observer notifications by sending * [EODelayedObserver-subjectChanged] to all registered observers * of the receiver. During the processing new enqueObserver: methods will * be honored. It is up toe the callers to ensure no loops result.
*This method is invoked automatically with EOObserverPrioritySixth * during each run loop after an invocation of [-enqueueObserver:].
*Note that unlike the reference implementation, we dequeue the * observer after dispatching [EODelayedObserver-subjectChanged].
*/ - (void)notifyObserversUpToPriority: (EOObserverPriority)priority { EOObserverPriority i = EOObserverPriorityFirst; EODelayedObserver *observer = nil; while (i <= priority) { observer = _queue[i]; if (observer) { [self dequeueObserver: observer]; [observer subjectChanged]; i = EOObserverPriorityFirst; } else { i++; } } } /** * Sets the run loop modes the receiver uses when registering * for [-notifyObserversUpToPriority:] during [-enqueueObserver:]. * (seeNSRunLoop
).
*/
- (void)setRunLoopModes: (NSArray *)modes
{
ASSIGN(_modes, modes);
}
/**
* Returns the run loop modes the receiver uses when registering
* for [-notifyObserversUpToPriority:] during [-enqueueObserver:].
* (see NSRunLoop
).
*/
- (NSArray *)runLoopModes
{
return _modes;
}
@end
/**
* This is a convenience class which is initialized with a target,
* an action method and a priority, to have the action method
* invoked on the target when the subjectChanged method is invoked
* by the EODelayedObserverQueue.
*/
@implementation EOObserverProxy
/**
* Initializes the receiver to dispatch action to target upon processing
* [-subjectChanged]. The receiver uses priority for the
* [EODelayedObserverQueue].
* Target is not retained.
*/
- (id)initWithTarget: (id)target
action: (SEL)action
priority: (EOObserverPriority)priority
{
if ((self = [super init]))
{
_target = target;
_action = action;
_priority = priority;
}
return self;
}
- (void) dealloc
{
[self discardPendingNotification];
[super dealloc];
}
/**
* Returns the priority the receiver was initialized with.
*/
- (EOObserverPriority)priority
{
return _priority;
}
/**
* Overridden to dispatch the action method to the target which
* the receiver was initialized with.
*/
- (void)subjectChanged
{
[_target performSelector: _action
withObject: self];
}
@end