2727// See comment in header.
2828#import " GTMNSObject+KeyValueObserving.h"
2929
30+ #import < libkern/OSAtomic.h>
3031#include < objc/runtime.h>
32+ #import < stdatomic.h>
3133
3234#import " GTMDefines.h"
3335#import " GTMDebugSelectorValidation.h"
@@ -62,10 +64,11 @@ - (id)dictionaryKeyForObserver:(id)observer
6264@end
6365
6466@interface GTMKeyValueObservingHelper : NSObject {
65- __weak id observer_;
67+ @private
68+ GTM_WEAK id observer_;
6669 SEL selector_;
6770 id userInfo_;
68- __weak id target_;
71+ GTM_WEAK id target_;
6972 NSString * keyPath_;
7073}
7174
@@ -103,10 +106,10 @@ - (instancetype)initWithObserver:(id)observer
103106 if ((self = [super init ])) {
104107 observer_ = observer;
105108 selector_ = selector;
106- userInfo_ = userInfo;
109+ userInfo_ = [ userInfo retain ] ;
107110
108111 target_ = target;
109- keyPath_ = keyPath;
112+ keyPath_ = [ keyPath retain ] ;
110113
111114 [target addObserver: self
112115 forKeyPath: keyPath
@@ -128,6 +131,9 @@ - (void)dealloc {
128131 _GTMDevLog (@" Didn't deregister %@ " , self);
129132 [self deregister ];
130133 }
134+ [userInfo_ release ];
135+ [keyPath_ release ];
136+ [super dealloc ];
131137}
132138
133139- (void )observeValueForKeyPath : (NSString *)keyPath
@@ -140,11 +146,8 @@ - (void)observeValueForKeyPath:(NSString *)keyPath
140146 ofObject: object
141147 userInfo: userInfo_
142148 change: change];
143- #pragma clang diagnostic push
144- #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
145- // We are good here because the selector should not return anything.
146149 [observer_ performSelector: selector_ withObject: notification];
147- # pragma clang diagnostic pop
150+ [notification release ];
148151 } else {
149152 // COV_NF_START
150153 // There's no way this should ever be called.
@@ -160,20 +163,26 @@ - (void)observeValueForKeyPath:(NSString *)keyPath
160163- (void )deregister {
161164 [target_ removeObserver: self forKeyPath: keyPath_];
162165 target_ = nil ;
163- observer_ = nil ;
164166}
165167
166168@end
167169
168170@implementation GTMKeyValueObservingCenter
169171
170172+ (instancetype )defaultCenter {
171- static dispatch_once_t onceToken;
172- static GTMKeyValueObservingCenter *center;
173- dispatch_once (&onceToken, ^{
174- center = [[self alloc ] init ];
175- });
176- return center;
173+ static _Atomic (GTMKeyValueObservingCenter *)center = nil ;
174+ if (!center) {
175+ // do a bit of clever atomic setting to make this thread safe
176+ // if two threads try to set simultaneously, one will fail
177+ // and the other will set things up so that the failing thread
178+ // gets the shared center
179+ GTMKeyValueObservingCenter *newCenter = [[self alloc ] init ];
180+ GTMKeyValueObservingCenter *expected = nil ;
181+ if (!atomic_compare_exchange_strong (¢er, &expected, newCenter)) {
182+ [newCenter release ]; // COV_NF_LINE no guarantee we'll hit this line
183+ }
184+ }
185+ return center;
177186}
178187
179188- (instancetype )init {
@@ -183,6 +192,14 @@ - (instancetype)init {
183192 return self;
184193}
185194
195+ // COV_NF_START
196+ // Singletons don't get deallocated
197+ - (void )dealloc {
198+ [observerHelpers_ release ];
199+ [super dealloc ];
200+ }
201+ // COV_NF_END
202+
186203- (id )dictionaryKeyForObserver : (id )observer
187204 ofObject : (id )target
188205 forKeyPath : (NSString *)keyPath
@@ -223,6 +240,7 @@ - (void)addObserver:(id)observer
223240 }
224241 [observerHelpers_ setObject: helper forKey: key];
225242 }
243+ [helper release ];
226244}
227245
228246- (void )removeObserver : (id )observer
@@ -320,13 +338,21 @@ - (instancetype)initWithKeyPath:(NSString *)keyPath
320338 change : (NSDictionary *)change {
321339 if ((self = [super init ])) {
322340 keyPath_ = [keyPath copy ];
323- object_ = object;
324- userInfo_ = userInfo;
325- change_ = change;
341+ object_ = [ object retain ] ;
342+ userInfo_ = [ userInfo retain ] ;
343+ change_ = [ change retain ] ;
326344 }
327345 return self;
328346}
329347
348+ - (void )dealloc {
349+ [keyPath_ release ];
350+ [object_ release ];
351+ [userInfo_ release ];
352+ [change_ release ];
353+ [super dealloc ];
354+ }
355+
330356- (instancetype )copyWithZone : (NSZone *)zone {
331357 return [[[self class ] allocWithZone: zone] initWithKeyPath: keyPath_
332358 ofObject: object_
@@ -422,41 +448,40 @@ @implementation NSObject (GTMDebugKeyValueObserving)
422448 _gtmDebugArrayRemoveObserver:fromObjectsAtIndexes:forKeyPath:);
423449
424450+ (void )load {
425- @autoreleasepool {
426-
427- NSDictionary *env = [[NSProcessInfo processInfo ] environment ];
428- id debugKeyValue = [env valueForKey: @" GTMDebugKVO" ];
429- BOOL debug = NO ;
430- if ([debugKeyValue isKindOfClass: [NSNumber class ]]) {
431- debug = [debugKeyValue intValue ] != 0 ? YES : NO ;
432- } else if ([debugKeyValue isKindOfClass: [NSString class ]]) {
433- debug = ([debugKeyValue hasPrefix: @" Y" ] || [debugKeyValue hasPrefix: @" T" ] ||
434- [debugKeyValue intValue ]);
435- }
436- Class cls = Nil ;
437- if (debug) {
438- cls = [NSObject class ];
439- SwizzleMethodsInClass (cls,
440- @selector (addObserver:forKeyPath:options:context: ),
441- @selector (_gtmDebugAddObserver:forKeyPath:options:context: ));
442- SwizzleMethodsInClass (cls,
443- @selector (removeObserver:forKeyPath: ),
444- @selector (_gtmDebugRemoveObserver:forKeyPath: ));
445- SwizzleMethodsInClass (cls,
446- @selector (willChangeValueForKey: ),
447- @selector (_gtmDebugWillChangeValueForKey: ));
448- SwizzleMethodsInClass (cls,
449- @selector (didChangeValueForKey: ),
450- @selector (_gtmDebugDidChangeValueForKey: ));
451- cls = [NSArray class ];
452- SwizzleMethodsInClass (cls,
453- @selector (addObserver:toObjectsAtIndexes:forKeyPath:options:context: ),
454- @selector (_gtmDebugArrayAddObserver:toObjectsAtIndexes:forKeyPath:options:context: ));
455- SwizzleMethodsInClass (cls,
456- @selector (removeObserver:fromObjectsAtIndexes:forKeyPath: ),
457- @selector (_gtmDebugArrayRemoveObserver:fromObjectsAtIndexes:forKeyPath: ));
458- }
451+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc ] init ];
452+ NSDictionary *env = [[NSProcessInfo processInfo ] environment ];
453+ id debugKeyValue = [env valueForKey: @" GTMDebugKVO" ];
454+ BOOL debug = NO ;
455+ if ([debugKeyValue isKindOfClass: [NSNumber class ]]) {
456+ debug = [debugKeyValue intValue ] != 0 ? YES : NO ;
457+ } else if ([debugKeyValue isKindOfClass: [NSString class ]]) {
458+ debug = ([debugKeyValue hasPrefix: @" Y" ] || [debugKeyValue hasPrefix: @" T" ] ||
459+ [debugKeyValue intValue ]);
460+ }
461+ Class cls = Nil ;
462+ if (debug) {
463+ cls = [NSObject class ];
464+ SwizzleMethodsInClass (cls,
465+ @selector (addObserver:forKeyPath:options:context: ),
466+ @selector (_gtmDebugAddObserver:forKeyPath:options:context: ));
467+ SwizzleMethodsInClass (cls,
468+ @selector (removeObserver:forKeyPath: ),
469+ @selector (_gtmDebugRemoveObserver:forKeyPath: ));
470+ SwizzleMethodsInClass (cls,
471+ @selector (willChangeValueForKey: ),
472+ @selector (_gtmDebugWillChangeValueForKey: ));
473+ SwizzleMethodsInClass (cls,
474+ @selector (didChangeValueForKey: ),
475+ @selector (_gtmDebugDidChangeValueForKey: ));
476+ cls = [NSArray class ];
477+ SwizzleMethodsInClass (cls,
478+ @selector (addObserver:toObjectsAtIndexes:forKeyPath:options:context: ),
479+ @selector (_gtmDebugArrayAddObserver:toObjectsAtIndexes:forKeyPath:options:context: ));
480+ SwizzleMethodsInClass (cls,
481+ @selector (removeObserver:fromObjectsAtIndexes:forKeyPath: ),
482+ @selector (_gtmDebugArrayRemoveObserver:fromObjectsAtIndexes:forKeyPath: ));
459483 }
484+ [pool drain ];
460485}
461486
462487- (void )_gtmDebugAddObserver : (NSObject *)observer
0 commit comments