![]() |
API 0.9.5
|
00001 /* 00002 * CPNotificationCenter.j 00003 * Foundation 00004 * 00005 * Created by Francisco Tolmasky. 00006 * Copyright 2008, 280 North, Inc. 00007 * 00008 * This library is free software; you can redistribute it and/or 00009 * modify it under the terms of the GNU Lesser General Public 00010 * License as published by the Free Software Foundation; either 00011 * version 2.1 of the License, or (at your option) any later version. 00012 * 00013 * This library is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 * Lesser General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU Lesser General Public 00019 * License along with this library; if not, write to the Free Software 00020 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00021 */ 00022 00023 00024 var CPNotificationDefaultCenter = nil; 00025 00039 @implementation CPNotificationCenter : CPObject 00040 { 00041 CPMutableDictionary _namedRegistries; 00042 _CPNotificationRegistry _unnamedRegistry; 00043 } 00044 00048 + (CPNotificationCenter)defaultCenter 00049 { 00050 if (!CPNotificationDefaultCenter) 00051 CPNotificationDefaultCenter = [[CPNotificationCenter alloc] init]; 00052 00053 return CPNotificationDefaultCenter; 00054 } 00055 00056 - (id)init 00057 { 00058 self = [super init]; 00059 00060 if (self) 00061 { 00062 _namedRegistries = [CPDictionary dictionary]; 00063 _unnamedRegistry = [[_CPNotificationRegistry alloc] init]; 00064 } 00065 return self; 00066 } 00067 00076 - (void)addObserver:(id)anObserver selector:(SEL)aSelector name:(CPString)aNotificationName object:(id)anObject 00077 { 00078 var registry, 00079 observer = [[_CPNotificationObserver alloc] initWithObserver:anObserver selector:aSelector]; 00080 00081 if (aNotificationName == nil) 00082 registry = _unnamedRegistry; 00083 00084 else if (!(registry = [_namedRegistries objectForKey:aNotificationName])) 00085 { 00086 registry = [[_CPNotificationRegistry alloc] init]; 00087 [_namedRegistries setObject:registry forKey:aNotificationName]; 00088 } 00089 00090 [registry addObserver:observer object:anObject]; 00091 } 00092 00097 - (void)removeObserver:(id)anObserver 00098 { 00099 var name = nil, 00100 names = [_namedRegistries keyEnumerator]; 00101 00102 while (name = [names nextObject]) 00103 [[_namedRegistries objectForKey:name] removeObserver:anObserver object:nil]; 00104 00105 [_unnamedRegistry removeObserver:anObserver object:nil]; 00106 } 00107 00114 - (void)removeObserver:(id)anObserver name:(CPString)aNotificationName object:(id)anObject 00115 { 00116 if (aNotificationName == nil) 00117 { 00118 var name = nil, 00119 names = [_namedRegistries keyEnumerator]; 00120 00121 while (name = [names nextObject]) 00122 [[_namedRegistries objectForKey:name] removeObserver:anObserver object:anObject]; 00123 00124 [_unnamedRegistry removeObserver:anObserver object:anObject]; 00125 } 00126 else 00127 [[_namedRegistries objectForKey:aNotificationName] removeObserver:anObserver object:anObject]; 00128 } 00129 00135 - (void)postNotification:(CPNotification)aNotification 00136 { 00137 if (!aNotification) 00138 [CPException raise:CPInvalidArgumentException reason:"postNotification: does not except 'nil' notifications"]; 00139 00140 _CPNotificationCenterPostNotification(self, aNotification); 00141 } 00142 00149 - (void)postNotificationName:(CPString)aNotificationName object:(id)anObject userInfo:(CPDictionary)aUserInfo 00150 { 00151 _CPNotificationCenterPostNotification(self, [[CPNotification alloc] initWithName:aNotificationName object:anObject userInfo:aUserInfo]); 00152 } 00153 00159 - (void)postNotificationName:(CPString)aNotificationName object:(id)anObject 00160 { 00161 _CPNotificationCenterPostNotification(self, [[CPNotification alloc] initWithName:aNotificationName object:anObject userInfo:nil]); 00162 } 00163 00164 @end 00165 00166 var _CPNotificationCenterPostNotification = function(/* CPNotificationCenter */ self, /* CPNotification */ aNotification) 00167 { 00168 [self._unnamedRegistry postNotification:aNotification]; 00169 [[self._namedRegistries objectForKey:[aNotification name]] postNotification:aNotification]; 00170 } 00171 00172 /* 00173 Mapping of Notification Name to listening object/selector. 00174 @ignore 00175 */ 00176 @implementation _CPNotificationRegistry : CPObject 00177 { 00178 CPDictionary _objectObservers; 00179 BOOL _observerRemovalCount; 00180 } 00181 00182 - (id)init 00183 { 00184 self = [super init]; 00185 00186 if (self) 00187 { 00188 _observerRemovalCount = 0; 00189 _objectObservers = [CPDictionary dictionary]; 00190 } 00191 00192 return self; 00193 } 00194 00195 - (void)addObserver:(_CPNotificationObserver)anObserver object:(id)anObject 00196 { 00197 // If there's no object, then we're listening to this 00198 // notification regardless of whom sends it. 00199 if (!anObject) 00200 anObject = [CPNull null]; 00201 00202 // Grab all the listeners for this notification/object pair 00203 var observers = [_objectObservers objectForKey:[anObject UID]]; 00204 00205 if (!observers) 00206 { 00207 observers = []; 00208 [_objectObservers setObject:observers forKey:[anObject UID]]; 00209 } 00210 00211 // Add this observer. 00212 observers.push(anObserver); 00213 } 00214 00215 - (void)removeObserver:(id)anObserver object:(id)anObject 00216 { 00217 var removedKeys = []; 00218 00219 // This means we're getting rid of EVERY instance of this observer. 00220 if (anObject == nil) 00221 { 00222 var key = nil, 00223 keys = [_objectObservers keyEnumerator]; 00224 00225 // Iterate through every set of observers 00226 while (key = [keys nextObject]) 00227 { 00228 var observers = [_objectObservers objectForKey:key], 00229 count = observers ? observers.length : 0; 00230 00231 while (count--) 00232 if ([observers[count] observer] == anObserver) 00233 { 00234 ++_observerRemovalCount; 00235 observers.splice(count, 1); 00236 } 00237 00238 if (!observers || observers.length == 0) 00239 removedKeys.push(key); 00240 } 00241 } 00242 else 00243 { 00244 var key = [anObject UID], 00245 observers = [_objectObservers objectForKey:key], 00246 count = observers ? observers.length : 0; 00247 00248 while (count--) 00249 if ([observers[count] observer] == anObserver) 00250 { 00251 ++_observerRemovalCount; 00252 observers.splice(count, 1) 00253 } 00254 00255 if (!observers || observers.length == 0) 00256 removedKeys.push(key); 00257 } 00258 00259 var count = removedKeys.length; 00260 00261 while (count--) 00262 [_objectObservers removeObjectForKey:removedKeys[count]]; 00263 } 00264 00265 - (void)postNotification:(CPNotification)aNotification 00266 { 00267 // We don't want to erroneously send notifications to observers that get removed 00268 // during the posting of this notification, nor observers that get added. The 00269 // best way to do this is to make a copy of the current observers (this avoids 00270 // new observers from being notified) and double checking every observer against 00271 // the current array (this avoids removed observers from receiving notifications). 00272 // However, this is a very expensive operation (O(N) => O(N^2)), so to avoid it, 00273 // we keep track of whether observers are added or removed, and only do our 00274 // rigorous testing in those cases. 00275 var observerRemovalCount = _observerRemovalCount, 00276 object = [aNotification object], 00277 observers = nil; 00278 00279 if (object != nil && (observers = [[_objectObservers objectForKey:[object UID]] copy])) 00280 { 00281 var currentObservers = observers, 00282 count = observers.length; 00283 00284 while (count--) 00285 { 00286 var observer = observers[count]; 00287 00288 // if there wasn't removal of an observer during this posting, or there 00289 // was but we are still in the observer list... 00290 if ((observerRemovalCount === _observerRemovalCount) || [currentObservers indexOfObjectIdenticalTo:observer] !== CPNotFound) 00291 [observer postNotification:aNotification]; 00292 } 00293 } 00294 00295 // Now do the same for the nil object observers... 00296 observers = [[_objectObservers objectForKey:[[CPNull null] UID]] copy]; 00297 00298 if (!observers) 00299 return; 00300 00301 var observerRemovalCount = _observerRemovalCount, 00302 count = observers.length, 00303 currentObservers = observers; 00304 00305 while (count--) 00306 { 00307 var observer = observers[count]; 00308 00309 // if there wasn't removal of an observer during this posting, or there 00310 // was but we are still in the observer list... 00311 if ((observerRemovalCount === _observerRemovalCount) || [currentObservers indexOfObjectIdenticalTo:observer] !== CPNotFound) 00312 [observer postNotification:aNotification]; 00313 } 00314 } 00315 00316 - (unsigned)count 00317 { 00318 return [_objectObservers count]; 00319 } 00320 00321 @end 00322 00323 /* @ignore */ 00324 @implementation _CPNotificationObserver : CPObject 00325 { 00326 id _observer; 00327 SEL _selector; 00328 } 00329 00330 - (id)initWithObserver:(id)anObserver selector:(SEL)aSelector 00331 { 00332 if (self) 00333 { 00334 _observer = anObserver; 00335 _selector = aSelector; 00336 } 00337 00338 return self; 00339 } 00340 00341 - (id)observer 00342 { 00343 return _observer; 00344 } 00345 00346 - (void)postNotification:(CPNotification)aNotification 00347 { 00348 [_observer performSelector:_selector withObject:aNotification]; 00349 } 00350 00351 @end