aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar gtm.daemon <gtm.daemon@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2009-08-11 18:30:29 +0000
committerGravatar gtm.daemon <gtm.daemon@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2009-08-11 18:30:29 +0000
commit1ddb352f1d7621deeb93848dedf034dc5d271a1f (patch)
tree0e3c060f447f687396ac3bb416cf02ae3b671f42
parenta4a6a27c10d9d78034c759678be0ebf95ff3d76c (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)
-rw-r--r--Foundation/GTMGoogleSearch.h4
-rw-r--r--Foundation/GTMGoogleSearch.m11
-rw-r--r--Foundation/GTMGoogleSearchTest.m15
-rw-r--r--Foundation/GTMNSObject+KeyValueObserving.m124
-rw-r--r--Foundation/GTMNSObject+KeyValueObservingTest.m3
-rw-r--r--Foundation/GTMObjC2Runtime.h17
-rw-r--r--GTMDefines.h23
-rw-r--r--ReleaseNotes.txt5
8 files changed, 168 insertions, 34 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)
diff --git a/GTMDefines.h b/GTMDefines.h
index 2bb4486..3c69547 100644
--- a/GTMDefines.h
+++ b/GTMDefines.h
@@ -29,21 +29,6 @@
#define MAC_OS_X_VERSION_10_6 1060
#endif
-// These definitions exist to allow headerdoc to parse this file.
-// Headerdoc 8.6 gives warnings about misuses of MAC_OS_X_VERSION_MIN_REQUIRED
-// and MAC_OS_X_VERSION_MAX_ALLOWED if you use them directly.
-// By defining GTM versions with slightly different names (MIN vs MINIMUM)
-// we get around headerdoc's issues. Hopefully we can work around this in the
-// future and get rid of the GTM versions, so please use the default ones
-// wherever you can.
-#ifndef GTM_MAC_OS_X_VERSION_MINIMUM_REQUIRED
- #define GTM_MAC_OS_X_VERSION_MINIMUM_REQUIRED MAC_OS_X_VERSION_MIN_REQUIRED
-#endif
-
-#ifndef GTM_MAC_OS_X_VERSION_MAXIMUM_ALLOWED
- #define GTM_MAC_OS_X_VERSION_MAXIMUM_ALLOWED MAC_OS_X_VERSION_MAX_ALLOWED
-#endif
-
// ----------------------------------------------------------------------------
// CPP symbols that can be overridden in a prefix to control how the toolbox
// is compiled.
@@ -165,7 +150,7 @@ GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);
// does keys, so pick the right thing, nothing is done on the FastEnumeration
// side to be sure you're getting what you wanted.
#ifndef GTM_FOREACH_OBJECT
- #if TARGET_OS_IPHONE || (GTM_MAC_OS_X_VERSION_MINIMUM_REQUIRED >= MAC_OS_X_VERSION_10_5)
+ #if TARGET_OS_IPHONE || !(MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5)
#define GTM_FOREACH_ENUMEREE(element, enumeration) \
for (element in enumeration)
#define GTM_FOREACH_OBJECT(element, collection) \
@@ -224,7 +209,7 @@ GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);
#else
// We can't find a symbol to tell if GC is supported/required, so best we
// do on Mac targets is include it if we're on 10.5 or later.
- #if GTM_MAC_OS_X_VERSION_MAXIMUM_ALLOWED <= MAC_OS_X_VERSION_10_4
+ #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
#define GTM_SUPPORT_GC 0
#else
#define GTM_SUPPORT_GC 1
@@ -234,7 +219,7 @@ GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);
// To simplify support for 64bit (and Leopard in general), we provide the type
// defines for non Leopard SDKs
-#if GTM_MAC_OS_X_VERSION_MAXIMUM_ALLOWED <= MAC_OS_X_VERSION_10_4
+#if !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
// NSInteger/NSUInteger and Max/Mins
#ifndef NSINTEGER_DEFINED
#if __LP64__ || NS_BUILD_32_LIKE_64
@@ -265,4 +250,4 @@ GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);
#endif /* !defined(__LP64__) || !__LP64__ */
#define CGFLOAT_DEFINED 1
#endif // CGFLOAT_DEFINED
-#endif // GTM_MAC_OS_X_VERSION_MAXIMUM_ALLOWED <= MAC_OS_X_VERSION_10_4
+#endif // MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt
index aa7083a..d2a30b1 100644
--- a/ReleaseNotes.txt
+++ b/ReleaseNotes.txt
@@ -316,6 +316,11 @@ Changes since 1.5.1
- Added gtm_dictionaryWithHttpArgumentsString to NSDictionary+URLArguments.
+- Added GTMDebugKeyValueObserving category to NSObject. This makes debugging
+ KVO a little easier in some cases. To turn it on, set the "GTMDebugKVO"
+ environment variable to "1". It will output a lot of data about adding and
+ removing KVO observers, and when the values are actually changed.
+
Release 1.5.1
Changes since 1.5.0