Luky/Comm/LLNotificationCenter.m

00001 //
00002 //  LLNotificationCenter.m
00003 //  MacTrek
00004 //
00005 //  Created by Aqua on 27/04/2006.
00006 //  Copyright 2006 Luky Soft. All rights reserved.
00007 //
00008 
00009 #import "LLNotificationCenter.h"
00010 
00011 
00012 @implementation LLNotificationCenter
00013 
00014 LLNotificationCenter *defaultCenter;
00015 NSLock *synchronizeAccess;
00016 NSMutableDictionary *senders;
00017 LLDOReceiver *receiver;
00018 NSString *mainThreadID;
00019 bool sendEvents = YES;
00020 
00021 - (id) init {
00022     self = [super init];
00023     if (self != nil) {
00024         listeners = [[NSMutableDictionary alloc] init];
00025         useLocks = NO; // default off
00026         synchronizeAccess = [[NSLock alloc] init];
00027         timeOut = 2; // default wait 2 seconds
00028         
00029         // this is the main run loop so set up the DO
00030         receiver = [[LLDOReceiver alloc] init];
00031         // this will be called from a thread when an event was posted
00032         [receiver setTarget:self withSelector:@selector(remotelyPostedEvent:)];
00033         // this will store the senders
00034         senders = [[NSMutableDictionary alloc] init];
00035         // and this is the id of the main thread
00036         mainThreadID = [NSString stringWithFormat:@"%d", [NSThread currentThread]];
00037         [mainThreadID retain];
00038     }
00039     return self;
00040 }
00041 
00042 + (LLNotificationCenter*) defaultCenter {
00043     if (defaultCenter == nil) {
00044         defaultCenter = [[LLNotificationCenter alloc] init];
00045     }
00046     return defaultCenter;
00047 }
00048 
00049 - (void)addObserver:(id)notificationObserver            // cannot be nil
00050            selector:(SEL)notificationSelector           // cannot be nil
00051                name:(NSString *)notificationName {
00052        
00053     [self addObserver:notificationObserver 
00054              selector:notificationSelector 
00055                  name:notificationName 
00056                object:nil 
00057              useLocks:NO
00058        useMainRunLoop:NO];
00059 }
00060 
00061 - (void)addObserver:(id)notificationObserver            // cannot be nil
00062            selector:(SEL)notificationSelector           // cannot be nil
00063                name:(NSString *)notificationName        // nil means all
00064              object:(NSString *)notificationSender {    // nil means of all
00065     
00066     [self addObserver:notificationObserver 
00067              selector:notificationSelector 
00068                  name:notificationName 
00069                object:notificationSender 
00070              useLocks:NO
00071        useMainRunLoop:NO];
00072 }
00073 
00074 - (void)addObserver:(id)notificationObserver            // cannot be nil
00075            selector:(SEL)notificationSelector           // cannot be nil
00076                name:(NSString *)notificationName        // nil means all
00077              object:(NSString *)notificationSender      // nil means of all
00078            useLocks:(bool)protect {
00079     [self addObserver:notificationObserver 
00080              selector:notificationSelector 
00081                  name:notificationName 
00082                object:notificationSender 
00083              useLocks:protect
00084        useMainRunLoop:NO];
00085 }
00086 
00087 - (void)addObserver:(id)notificationObserver            // cannot be nil
00088            selector:(SEL)notificationSelector           // cannot be nil
00089                name:(NSString *)notificationName        // nil means all
00090              object:(NSString *)notificationSender      // nil means of all
00091            useLocks:(bool)protect 
00092   useMainRunLoop:(bool)useMainLoop {                                                     
00093     
00094     if (notificationObserver == nil) {
00095         NSLog(@"LLNotificationCenter.addObserver notificationObserver cannot be nil");
00096         return;
00097     } 
00098     if (notificationSelector == nil) {
00099         NSLog(@"LLNotificationCenter.addObserver notificationSelector cannot be nil");
00100         return;
00101     }
00102     // little optimalization, when using the mainloop it is thread safe anyway
00103     if (useMainLoop) {
00104         protect = NO; // no need for locks AND DO
00105     }
00106     
00107     // create a new entry
00108     LLNotificationCenterEntry *newListener = [[LLNotificationCenterEntry alloc] initWithNotificationName:notificationName
00109                                                                                                   source:notificationSender 
00110                                                                                                   target:notificationObserver
00111                                                                                                 selector:notificationSelector
00112                                                                                                 useLocks:protect
00113                                                                                              useMainLoop:useMainLoop];
00114 
00115     // see if anyone is listening
00116     NSMutableArray *listenersForThisKey = [listeners valueForKey:notificationName];
00117     if (listenersForThisKey == nil) {
00118         // first one ? create table
00119         listenersForThisKey = [[NSMutableArray alloc] init];
00120         [listeners setValue: listenersForThisKey forKey:notificationName];
00121     }
00122     
00123     // add us to these listeners
00124     [listenersForThisKey addObject:newListener];
00125     
00126     return;    
00127 }    
00128 
00129 - (void) remotelyPostedEvent:(LLNotificationCenterEntry *)listener {
00130     
00131     //NSLog(@"LLNotificationCenter.remotelyPostedEvent %@", [listener name]); 
00132     
00133     // get the parameters passed over the DO
00134     id target = [listener target];
00135     SEL selector = [listener selector];
00136     id userInfo = [listener userData];
00137     
00138     // invoke the selector (we are now in the main loop)
00139     [target performSelector:selector withObject:userInfo];
00140 }
00141 
00142 - (LLDOSender *) getSenderForMyThread {
00143     // create a new one for every event.... $$
00144     NSString *threadID = [NSString stringWithFormat:@"%d", [NSThread currentThread]]; 
00145     
00146     // for the main thread do nothing
00147     if ([threadID isEqualToString: mainThreadID]) {
00148         return nil;
00149     }
00150     
00151     LLDOSender *sender = [senders objectForKey:threadID];
00152     if (sender == nil) {
00153         // new thread? create a seperate sender
00154         NSLog(@"LLNotificationCenter.getSenderForMyThread creating sender for thread %d", threadID);
00155         sender = [[LLDOSender alloc] init];
00156         [senders setObject:sender forKey:threadID];
00157     }
00158     return sender;
00159 }
00160 
00161 - (void) setEnable:(bool)enable {
00162     sendEvents = enable;
00163 }
00164 
00165 - (void) postNotificationName:(NSString *)name object:(id) sender userInfo:(id)userInfo {
00166     
00167     if (!sendEvents) {
00168         return;
00169     }
00170     
00171     // debug print, very helpfull, but takes load
00172     //NSLog(@"LLNotificationCenter.postNotificationName %@", name);
00173     
00174     bool hasObserver = NO;
00175     
00176     if (name == nil) {
00177         NSLog(@"LLNotificationCenter.postNotification nil strings are no longer accepted");
00178         return;
00179     }
00180     
00181     NSMutableArray *listenersForThisKey = [listeners valueForKey:name];
00182     
00183     // now execute all listeners
00184     for (int i = 0; i < [listenersForThisKey count]; i++) {
00185         LLNotificationCenterEntry *listener = [listenersForThisKey objectAtIndex:i];
00186         
00187         // filter on source first
00188         if (([listener source] == sender) || ([listener source] == nil)) { // nil means all source
00189             hasObserver = YES;
00190             id target = [listener target];
00191             SEL selector = [listener selector];                    
00192             // check for the use of locks (protect multithreading)
00193             if (useLocks || [listener useLocks]) {
00194                 // if we use locks, use a timeout
00195                 if ([synchronizeAccess lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:timeOut]]) {
00196                     //[synchronizeAccess lock]; // lock was obtained, don't lock twice
00197                     [target performSelector:selector withObject:userInfo];       // invoke 
00198                     [synchronizeAccess unlock]; // but unlock                        
00199                 } else {
00200                     NSLog(@"LLNotificationCenter.postNotificationName waited %d seconds for lock, discarding event");
00201                     return; // no lock obtained, so no need to unlock
00202                 }
00203             } else {
00204                 if ([listener useMainLoop]) {
00205                     // this event could have occured in a seperate thread as the main thread
00206                     // so we need to perform the execution over the DO
00207                     
00208                     // store the data
00209                     [listener setUserData:userInfo]; 
00210                     // get the sender
00211                     LLDOSender *sender = [self getSenderForMyThread];
00212                     // send the notification over the do
00213                     if (sender != nil) {
00214                         [sender invokeRemoteObjectWithUserData:listener];
00215                     } else { // try something
00216                         [target performSelector:selector withObject:userInfo];       // normal invoke
00217                     }                        
00218                     
00219                 } else {
00220                     [target performSelector:selector withObject:userInfo];       // normal invoke 
00221                 }                 
00222             }
00223         } 
00224     }
00225     if (!hasObserver) { // avoid this to speed up things
00226        NSLog(@"LLNotificationCenter.postNotificationName WARNING notification %@ has no observer", name);
00227     }
00228 }
00229 
00230 - (void)removeObserver:(id)notificationObserver        
00231                   name:(NSString *)notificationName {
00232     NSLog(@"LLNotificationCenter.removeObserver %@", notificationObserver); 
00233     
00234     int removed = 0;
00235     // go through entire dictionairy
00236     // not needed if notificationName != nil
00237     // but as that is usually the case, we better keep it simple
00238     unsigned int i, count = [[listeners allKeys] count];
00239     for (i = 0; i < count; i++) {
00240         
00241         // $$ hmm could do this outside the loop
00242         NSMutableArray *toBeRemovedListners = [[NSMutableArray alloc] init];   
00243         
00244         NSString *key = [[listeners allKeys] objectAtIndex:i];
00245         NSMutableArray *listenersForThisKey = [listeners objectForKey:key];
00246         
00247         // check for every key entry if we are the observer
00248         for (int i = 0; i < [listenersForThisKey count]; i++) {
00249             LLNotificationCenterEntry *listener = [listenersForThisKey objectAtIndex:i];        
00250             
00251             if ([listener target] == notificationObserver) {
00252                 // found the right lister target
00253                 if ([[listener name] isEqualToString:notificationName] ||
00254                     (notificationName == nil)) {
00255                     // notification name is ok
00256                     [toBeRemovedListners addObject:listener];              
00257                 }
00258             }
00259         }
00260         
00261         // remove
00262         [listenersForThisKey removeObjectsInArray:toBeRemovedListners];
00263         removed += [toBeRemovedListners count];
00264         [toBeRemovedListners release];
00265         
00266         // check if any left, if none, remove index
00267         /* $$ can't since we the index would shift and that would 
00268               mean that the for loop would fail
00269         if ([listenersForThisKey count] == 0) {
00270             [listeners removeObjectForKey:key];
00271             [listenersForThisKey release];
00272         }
00273          */
00274     }   
00275     NSLog(@"LLNotificationCenter.removeObserver removed %d occurences", removed);
00276 }
00277 
00278 - (void) postNotificationName:(NSString *)name {
00279     [self postNotificationName:name object:self userInfo:nil];
00280 }
00281 
00282 - (void) postNotificationName:(NSString *)name userInfo:(id)userInfo {
00283     [self postNotificationName:name object:self userInfo:userInfo];
00284 }
00285 
00286 - (bool) useLocks {
00287     return useLocks;
00288 }
00289 
00290 - (void) setUseLocks:(bool)protect {
00291     useLocks = protect;
00292 }
00293 
00294 - (int)  timeOut {
00295     return timeOut;
00296 }
00297 
00298 - (void) setTimeOut:(int)interval {
00299     timeOut = interval;
00300 }
00301 
00302 @end

Generated on Sat Aug 26 21:14:15 2006 for MacTrek by  doxygen 1.4.7