diff options
author | gtm.daemon <gtm.daemon@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3> | 2009-08-11 18:30:29 +0000 |
---|---|---|
committer | gtm.daemon <gtm.daemon@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3> | 2009-08-11 18:30:29 +0000 |
commit | 1ddb352f1d7621deeb93848dedf034dc5d271a1f (patch) | |
tree | 0e3c060f447f687396ac3bb416cf02ae3b671f42 /Foundation | |
parent | a4a6a27c10d9d78034c759678be0ebf95ff3d76c (diff) |
[Author: dmaclach]
Fixed up GTMGoogleSearch so that it actually works on the iPhone Device.
Fixed up some GTMNSObject+KVO issues on the iPhone Device.
Fixed up GTMObjC2Runtime so that it actually compiles on the device.
Added debugging capabilities to KVO with GTM.
Removed unnecessary GTM_* system version macros from GTMDefines.h
R=thomasvl
DELTA=185 (153 added, 19 deleted, 13 changed)
Diffstat (limited to 'Foundation')
-rw-r--r-- | Foundation/GTMGoogleSearch.h | 4 | ||||
-rw-r--r-- | Foundation/GTMGoogleSearch.m | 11 | ||||
-rw-r--r-- | Foundation/GTMGoogleSearchTest.m | 15 | ||||
-rw-r--r-- | Foundation/GTMNSObject+KeyValueObserving.m | 124 | ||||
-rw-r--r-- | Foundation/GTMNSObject+KeyValueObservingTest.m | 3 | ||||
-rw-r--r-- | Foundation/GTMObjC2Runtime.h | 17 |
6 files changed, 159 insertions, 15 deletions
diff --git a/Foundation/GTMGoogleSearch.h b/Foundation/GTMGoogleSearch.h index d18aadf..06b8d51 100644 --- a/Foundation/GTMGoogleSearch.h +++ b/Foundation/GTMGoogleSearch.h @@ -32,6 +32,10 @@ #define GTMGoogleSearchBooks @"books" #define GTMGoogleSearchWeb @"search" +// iPhone doesn't support distributed notifications, so this controls whether +// or not we enable them in this class. +#define GTM_GOOGLE_SEARCH_SUPPORTS_DISTRIBUTED_NOTIFICATIONS GTM_MACOS_SDK + // Composes URLs and searches for google properties in the correct language // and domain. @interface GTMGoogleSearch : NSObject { diff --git a/Foundation/GTMGoogleSearch.m b/Foundation/GTMGoogleSearch.m index 586efd3..56dcc82 100644 --- a/Foundation/GTMGoogleSearch.m +++ b/Foundation/GTMGoogleSearch.m @@ -131,6 +131,7 @@ GTMOBJECT_SINGLETON_BOILERPLATE(GTMGoogleSearch, sharedInstance); - (id)init { self = [super init]; if (self != nil) { +#if GTM_GOOGLE_SEARCH_SUPPORTS_DISTRIBUTED_NOTIFICATIONS // register for the notification NSDistributedNotificationCenter *distCenter = [NSDistributedNotificationCenter defaultCenter]; @@ -138,7 +139,7 @@ GTMOBJECT_SINGLETON_BOILERPLATE(GTMGoogleSearch, sharedInstance); selector:@selector(reloadAllAppCachedValues:) name:kNotificationName object:nil]; - +#endif // GTM_GOOGLE_SEARCH_SUPPORTS_DISTRIBUTED_NOTIFICATIONS // load the allApps value [self reloadAllAppCachedValues:nil]; @@ -175,13 +176,17 @@ GTMOBJECT_SINGLETON_BOILERPLATE(GTMGoogleSearch, sharedInstance); return self; } +#if GTM_GOOGLE_SEARCH_SUPPORTS_DISTRIBUTED_NOTIFICATIONS - (void)finalize { [[NSDistributedNotificationCenter defaultCenter] removeObject:self]; [super finalize]; } +#endif // GTM_GOOGLE_SEARCH_SUPPORTS_DISTRIBUTED_NOTIFICATIONS - (void)dealloc { +#if GTM_GOOGLE_SEARCH_SUPPORTS_DISTRIBUTED_NOTIFICATIONS [[NSDistributedNotificationCenter defaultCenter] removeObject:self]; +#endif // GTM_GOOGLE_SEARCH_SUPPORTS_DISTRIBUTED_NOTIFICATIONS [allAppsCachedDomain_ release]; [allAppsCachedLanguage_ release]; [curAppCachedDomain_ release]; @@ -341,7 +346,7 @@ GTMOBJECT_SINGLETON_BOILERPLATE(GTMGoogleSearch, sharedInstance); NSNull *nsNull = [NSNull null]; GTM_FOREACH_KEY(key, args) { NSString *object = [args objectForKey:key]; - if (![object isEqualTo:nsNull]) { + if (![object isEqual:nsNull]) { #if DEBUG // In debug we check key and object for things that should be escaped. // Note that percent is not in there because escaped strings will have @@ -515,6 +520,7 @@ GTMOBJECT_SINGLETON_BOILERPLATE(GTMGoogleSearch, sharedInstance); allAppsCachedDomain_ = [domain copy]; allAppsCachedLanguage_ = [language copy]; +#if GTM_GOOGLE_SEARCH_SUPPORTS_DISTRIBUTED_NOTIFICATIONS // NOTE: we'll go ahead and reload when this comes back to ourselves since // there is a race here if two folks wrote at about the same time. NSDistributedNotificationCenter *distCenter = @@ -522,6 +528,7 @@ GTMOBJECT_SINGLETON_BOILERPLATE(GTMGoogleSearch, sharedInstance); [distCenter postNotificationName:kNotificationName object:nil userInfo:nil]; +#endif // GTM_GOOGLE_SEARCH_SUPPORTS_DISTRIBUTED_NOTIFICATIONS } @end diff --git a/Foundation/GTMGoogleSearchTest.m b/Foundation/GTMGoogleSearchTest.m index 8e737c9..45f7e66 100644 --- a/Foundation/GTMGoogleSearchTest.m +++ b/Foundation/GTMGoogleSearchTest.m @@ -166,15 +166,20 @@ STAssertEqualObjects(domain, @"domain", nil); STAssertEqualObjects(lang, @"lang", nil); STAssertFalse(areCurrentAppOnly, nil); +#if GTM_GOOGLE_SEARCH_SUPPORTS_DISTRIBUTED_NOTIFICATIONS + // We don't test launching other tasks on the phone since this isn't a valid + // case until we can support real multiple tasks on the phone. // try changing the value directly in the plist file (as if another app had // done it) and sending our notification. [[NSTask launchedTaskWithLaunchPath:@"/usr/bin/defaults" arguments:[NSArray arrayWithObjects:@"write", @"com.google.GoogleSearchAllApps", - @"{ \"com.google.PreferredDomain\" = xxx; \"com.google.PreferredLanguage\" = yyy; }", + @"{ \"com.google.PreferredDomain\" = xxx;" + @"\"com.google.PreferredLanguage\" = yyy; }", nil]] waitUntilExit]; - // Sleep for a moment to let things flush (seen rarely as a problem on aharper's machine) + // Sleep for a moment to let things flush + // (seen rarely as a problem on aharper's machine). sleep(1); NSDistributedNotificationCenter *distCenter = [NSDistributedNotificationCenter defaultCenter]; @@ -184,7 +189,8 @@ options:NSNotificationDeliverImmediately]; // Spin the runloop so the notifications fire. - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]]; + NSRunLoop *currentLoop = [NSRunLoop currentRunLoop]; + [currentLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]]; // did we get what we expected? [googleSearch preferredDomain:&domain language:&lang @@ -192,7 +198,8 @@ STAssertEqualObjects(domain, @"xxx", nil); STAssertEqualObjects(lang, @"yyy", nil); STAssertFalse(areCurrentAppOnly, nil); - +#endif // GTM_GOOGLE_SEARCH_SUPPORTS_DISTRIBUTED_NOTIFICATIONS + // lastly, clean up what we set for all apps to leave the system at the // default. [googleSearch clearPreferredDomainAndLanguageForAllApps]; diff --git a/Foundation/GTMNSObject+KeyValueObserving.m b/Foundation/GTMNSObject+KeyValueObserving.m index dc6883d..e164e03 100644 --- a/Foundation/GTMNSObject+KeyValueObserving.m +++ b/Foundation/GTMNSObject+KeyValueObserving.m @@ -27,8 +27,6 @@ // See comment in header. #import "GTMNSObject+KeyValueObserving.h" - -#import <libkern/OSAtomic.h> #import "GTMDefines.h" #import "GTMDebugSelectorValidation.h" #import "GTMObjC2Runtime.h" @@ -115,6 +113,13 @@ static char* GTMKeyValueObservingHelperContext return self; } +- (NSString *)description { + return [NSString stringWithFormat: + @"%@ <observer = %@ keypath = %@ target = %@ selector = %@>", + [self class], observer_, keyPath_, target_, + NSStringFromSelector(selector_)]; +} + - (void)dealloc { [userInfo_ release]; [keyPath_ release]; @@ -327,6 +332,12 @@ static char* GTMKeyValueObservingHelperContext && [change_ isEqual:[object change]]); } +- (NSString *)description { + return [NSString stringWithFormat: + @"%@ <object = %@ keypath = %@ userInfo = %@ change = %@>", + [self class], object_, keyPath_, userInfo_, change_]; +} + - (NSUInteger)hash { return [keyPath_ hash] + [object_ hash] + [userInfo_ hash] + [change_ hash]; } @@ -348,3 +359,112 @@ static char* GTMKeyValueObservingHelperContext } @end + +#ifdef DEBUG + +// This category exists to attempt to help debug tricky KVO issues. +// Set the GTMDebugKVO environment variable to 1 and you will get a whole +// pile of KVO notifications that may help you track down problems. +@interface NSObject (GTMDebugKeyValueObserving) +@end + +@implementation NSObject (GTMDebugKeyValueObserving) ++ (void)load { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSDictionary *env = [[NSProcessInfo processInfo] environment]; + id debugKeyValue = [env valueForKey:@"GTMDebugKVO"]; + BOOL debug = NO; + if ([debugKeyValue isKindOfClass:[NSNumber class]]) { + debug = [debugKeyValue intValue] != 0 ? YES : NO; + } else if ([debugKeyValue isKindOfClass:[NSString class]]) { + debug = ([debugKeyValue hasPrefix:@"Y"] || [debugKeyValue hasPrefix:@"T"] || + [debugKeyValue intValue]); + } + if (debug) { + Class cls = [NSObject class]; + SEL addSelector + = NSSelectorFromString(@"addObserver:forKeyPath:options:context:"); + SEL removeSelector = NSSelectorFromString(@"removeObserver:forKeyPath:"); + SEL debugAddSelector + = NSSelectorFromString(@"_gtmDebugAddObserver:forKeyPath:options:context:"); + SEL debugRemoveSelector + = NSSelectorFromString(@"_gtmDebugRemoveObserver:forKeyPath:"); + SEL willChangeValueSelector = NSSelectorFromString(@"willChangeValueForKey:"); + SEL didChangeValueSelector = NSSelectorFromString(@"didChangeValueForKey:"); + SEL debugWillChangeValueSelector + = NSSelectorFromString(@"_gtmDebugWillChangeValueForKey:"); + SEL debugDidChangeValueSelector + = NSSelectorFromString(@"_gtmDebugDidChangeValueForKey:"); + + Method m1 = class_getInstanceMethod(cls, addSelector); + Method m2 = class_getInstanceMethod(cls, debugAddSelector); + method_exchangeImplementations(m1, m2); + m1 = class_getInstanceMethod(cls, removeSelector); + m2 = class_getInstanceMethod(cls, debugRemoveSelector); + method_exchangeImplementations(m1, m2); + m1 = class_getInstanceMethod(cls, willChangeValueSelector); + m2 = class_getInstanceMethod(cls, debugWillChangeValueSelector); + method_exchangeImplementations(m1, m2); + m1 = class_getInstanceMethod(cls, didChangeValueSelector); + m2 = class_getInstanceMethod(cls, debugDidChangeValueSelector); + method_exchangeImplementations(m1, m2); + + debugAddSelector + = NSSelectorFromString(@"_gtmDebugArrayAddObserver:forKeyPath:options:context:"); + debugRemoveSelector + = NSSelectorFromString(@"_gtmDebugArrayRemoveObserver:forKeyPath:"); + + cls = [NSArray class]; + m1 = class_getInstanceMethod(cls, addSelector); + m2 = class_getInstanceMethod(cls, debugAddSelector); + method_exchangeImplementations(m1, m2); + m1 = class_getInstanceMethod(cls, removeSelector); + m2 = class_getInstanceMethod(cls, debugRemoveSelector); + method_exchangeImplementations(m1, m2); + } + [pool release]; +} + +- (void)_gtmDebugAddObserver:(NSObject *)observer + forKeyPath:(NSString *)keyPath + options:(NSKeyValueObservingOptions)options + context:(void *)context { + _GTMDevLog(@"Adding observer %@ to %@ keypath '%@'", observer, self, keyPath); + [self _gtmDebugAddObserver:observer forKeyPath:keyPath + options:options context:context]; +} + +- (void)_gtmDebugArrayAddObserver:(NSObject *)observer + forKeyPath:(NSString *)keyPath + options:(NSKeyValueObservingOptions)options + context:(void *)context { + _GTMDevLog(@"Array adding observer %@ to %@ keypath '%@'", observer, self, keyPath); + [self _gtmDebugArrayAddObserver:observer forKeyPath:keyPath + options:options context:context]; +} + +- (void)_gtmDebugRemoveObserver:(NSObject *)observer + forKeyPath:(NSString *)keyPath { + _GTMDevLog(@"Removing observer %@ from %@ keypath '%@'", observer, self, keyPath); + [self _gtmDebugRemoveObserver:observer forKeyPath:keyPath]; +} + +- (void)_gtmDebugArrayRemoveObserver:(NSObject *)observer + forKeyPath:(NSString *)keyPath { + _GTMDevLog(@"Array removing observer %@ from %@ keypath '%@'", observer, self, keyPath); + [self _gtmDebugArrayRemoveObserver:observer forKeyPath:keyPath]; +} + +- (void)_gtmDebugWillChangeValueForKey:(NSString*)key { + _GTMDevLog(@"Will change '%@' of %@", key, self); + [self _gtmDebugWillChangeValueForKey:key]; +} + +- (void)_gtmDebugDidChangeValueForKey:(NSString*)key { + _GTMDevLog(@"Did change '%@' of %@", key, self); + [self _gtmDebugDidChangeValueForKey:key]; +} + +#endif // DEBUG + +@end diff --git a/Foundation/GTMNSObject+KeyValueObservingTest.m b/Foundation/GTMNSObject+KeyValueObservingTest.m index b97f7ff..ec35bc3 100644 --- a/Foundation/GTMNSObject+KeyValueObservingTest.m +++ b/Foundation/GTMNSObject+KeyValueObservingTest.m @@ -87,6 +87,9 @@ selector:@selector(observeValueChange:) userInfo:@"userInfo" options:NSKeyValueObservingOptionNew]; + [dict_ gtm_removeObserver:self + forKeyPath:@"key" + selector:@selector(observeValueChange:)]; } - (void)observeValueChange:(GTMKeyValueChangeNotification *)notification { diff --git a/Foundation/GTMObjC2Runtime.h b/Foundation/GTMObjC2Runtime.h index 2c25841..4f569a5 100644 --- a/Foundation/GTMObjC2Runtime.h +++ b/Foundation/GTMObjC2Runtime.h @@ -75,7 +75,7 @@ OBJC_EXPORT struct objc_method_description protocol_getMethodDescription(Protoco // barrier versions. GTM_INLINE bool OSAtomicCompareAndSwapPtrBarrier(void *predicate, void *replacement, - volatile void *theValue) { + void * volatile *theValue) { #if defined(__LP64__) && __LP64__ return OSAtomicCompareAndSwap64Barrier((int64_t)predicate, (int64_t)replacement, @@ -86,21 +86,24 @@ GTM_INLINE bool OSAtomicCompareAndSwapPtrBarrier(void *predicate, (int32_t *)theValue); #endif // defined(__LP64__) && __LP64__ } - + +#endif // MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 +#endif // MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 + +#if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) || (GTM_IPHONE_DEVICE) + GTM_INLINE BOOL objc_atomicCompareAndSwapGlobalBarrier(id predicate, id replacement, volatile id *objectLocation) { return OSAtomicCompareAndSwapPtrBarrier(predicate, replacement, - objectLocation); + (void * volatile *)objectLocation); } GTM_INLINE BOOL objc_atomicCompareAndSwapInstanceVariableBarrier(id predicate, id replacement, volatile id *objectLocation) { return OSAtomicCompareAndSwapPtrBarrier(predicate, replacement, - objectLocation); + (void * volatile *)objectLocation); } -#endif - -#endif // OBJC2_UNAVAILABLE +#endif // (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) || (GTM_IPHONE_DEVICE) |