/core/externals/update-engine/externals/google-toolbox-for-mac/Foundation/GTMNSObject+KeyValueObserving.m

http://macfuse.googlecode.com/ · Objective C · 676 lines · 504 code · 77 blank · 95 comment · 36 complexity · 08992f313278a6ea9a3f390f26309bb4 MD5 · raw file

  1. //
  2. // GTMNSObject+KeyValueObserving.m
  3. //
  4. // Copyright 2009 Google Inc.
  5. //
  6. // Licensed under the Apache License, Version 2.0 (the "License"); you may not
  7. // use this file except in compliance with the License. You may obtain a copy
  8. // of the License at
  9. //
  10. // http://www.apache.org/licenses/LICENSE-2.0
  11. //
  12. // Unless required by applicable law or agreed to in writing, software
  13. // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  14. // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  15. // License for the specific language governing permissions and limitations under
  16. // the License.
  17. //
  18. //
  19. // MAKVONotificationCenter.m
  20. // MAKVONotificationCenter
  21. //
  22. // Created by Michael Ash on 10/15/08.
  23. //
  24. // This code is based on code by Michael Ash.
  25. // See comment in header.
  26. #import "GTMDefines.h"
  27. #import "GTMNSObject+KeyValueObserving.h"
  28. #import "GTMDefines.h"
  29. #import "GTMDebugSelectorValidation.h"
  30. #import "GTMObjC2Runtime.h"
  31. #import "GTMMethodCheck.h"
  32. // A singleton that works as a dispatch center for KVO
  33. // -[NSObject observeValueForKeyPath:ofObject:change:context:] and turns them
  34. // into selector dispatches. It stores a collection of
  35. // GTMKeyValueObservingHelpers, and keys them via the key generated by
  36. // -dictionaryKeyForObserver:ofObject:forKeyPath:selector.
  37. @interface GTMKeyValueObservingCenter : NSObject {
  38. @private
  39. NSMutableDictionary *observerHelpers_;
  40. }
  41. + (id)defaultCenter;
  42. - (void)addObserver:(id)observer
  43. ofObject:(id)target
  44. forKeyPath:(NSString *)keyPath
  45. selector:(SEL)selector
  46. userInfo:(id)userInfo
  47. options:(NSKeyValueObservingOptions)options;
  48. - (void)removeObserver:(id)observer
  49. ofObject:(id)target
  50. forKeyPath:(NSString *)keyPath
  51. selector:(SEL)selector;
  52. - (id)dictionaryKeyForObserver:(id)observer
  53. ofObject:(id)target
  54. forKeyPath:(NSString *)keyPath
  55. selector:(SEL)selector;
  56. @end
  57. @interface GTMKeyValueObservingHelper : NSObject {
  58. @private
  59. __weak id observer_;
  60. SEL selector_;
  61. id userInfo_;
  62. __weak id target_;
  63. NSString* keyPath_;
  64. }
  65. - (id)initWithObserver:(id)observer
  66. object:(id)target
  67. keyPath:(NSString *)keyPath
  68. selector:(SEL)selector
  69. userInfo:(id)userInfo
  70. options:(NSKeyValueObservingOptions)options;
  71. - (void)deregister;
  72. @end
  73. @interface GTMKeyValueChangeNotification ()
  74. - (id)initWithKeyPath:(NSString *)keyPath ofObject:(id)object
  75. userInfo:(id)userInfo change:(NSDictionary *)change;
  76. @end
  77. @implementation GTMKeyValueObservingHelper
  78. // For info how and why we use these statics:
  79. // http://lists.apple.com/archives/cocoa-dev/2006/Jul/msg01038.html
  80. static char GTMKeyValueObservingHelperContextData;
  81. static char* GTMKeyValueObservingHelperContext
  82. = &GTMKeyValueObservingHelperContextData;
  83. - (id)initWithObserver:(id)observer
  84. object:(id)target
  85. keyPath:(NSString *)keyPath
  86. selector:(SEL)selector
  87. userInfo:(id)userInfo
  88. options:(NSKeyValueObservingOptions)options {
  89. if((self = [super init])) {
  90. observer_ = observer;
  91. selector_ = selector;
  92. userInfo_ = [userInfo retain];
  93. target_ = target;
  94. keyPath_ = [keyPath retain];
  95. [target addObserver:self
  96. forKeyPath:keyPath
  97. options:options
  98. context:GTMKeyValueObservingHelperContext];
  99. }
  100. return self;
  101. }
  102. - (NSString *)description {
  103. return [NSString stringWithFormat:
  104. @"%@ <observer = %@ keypath = %@ target = %@ selector = %@>",
  105. [self class], observer_, keyPath_, target_,
  106. NSStringFromSelector(selector_)];
  107. }
  108. - (void)dealloc {
  109. if (target_) {
  110. _GTMDevLog(@"Didn't deregister %@", self);
  111. [self deregister];
  112. }
  113. [userInfo_ release];
  114. [keyPath_ release];
  115. [super dealloc];
  116. }
  117. - (void)observeValueForKeyPath:(NSString *)keyPath
  118. ofObject:(id)object
  119. change:(NSDictionary *)change
  120. context:(void *)context {
  121. if(context == GTMKeyValueObservingHelperContext) {
  122. GTMKeyValueChangeNotification *notification
  123. = [[GTMKeyValueChangeNotification alloc] initWithKeyPath:keyPath
  124. ofObject:object
  125. userInfo:userInfo_
  126. change:change];
  127. [observer_ performSelector:selector_ withObject:notification];
  128. [notification release];
  129. } else {
  130. // COV_NF_START
  131. // There's no way this should ever be called.
  132. // If it is, the call will go up to NSObject which will assert.
  133. [super observeValueForKeyPath:keyPath
  134. ofObject:object
  135. change:change
  136. context:context];
  137. // COV_NF_END
  138. }
  139. }
  140. - (void)deregister {
  141. [target_ removeObserver:self forKeyPath:keyPath_];
  142. target_ = nil;
  143. }
  144. @end
  145. @implementation GTMKeyValueObservingCenter
  146. + (id)defaultCenter {
  147. static GTMKeyValueObservingCenter *center = nil;
  148. if(!center) {
  149. // do a bit of clever atomic setting to make this thread safe
  150. // if two threads try to set simultaneously, one will fail
  151. // and the other will set things up so that the failing thread
  152. // gets the shared center
  153. GTMKeyValueObservingCenter *newCenter = [[self alloc] init];
  154. if(!objc_atomicCompareAndSwapGlobalBarrier(nil,
  155. newCenter,
  156. (void *)&center)) {
  157. [newCenter release]; // COV_NF_LINE no guarantee we'll hit this line
  158. }
  159. }
  160. return center;
  161. }
  162. - (id)init {
  163. if((self = [super init])) {
  164. observerHelpers_ = [[NSMutableDictionary alloc] init];
  165. }
  166. return self;
  167. }
  168. // COV_NF_START
  169. // Singletons don't get deallocated
  170. - (void)dealloc {
  171. [observerHelpers_ release];
  172. [super dealloc];
  173. }
  174. // COV_NF_END
  175. - (id)dictionaryKeyForObserver:(id)observer
  176. ofObject:(id)target
  177. forKeyPath:(NSString *)keyPath
  178. selector:(SEL)selector {
  179. NSString *key = nil;
  180. if (!target && !keyPath && !selector) {
  181. key = [NSString stringWithFormat:@"%p:", observer];
  182. } else {
  183. key = [NSString stringWithFormat:@"%p:%@:%p:%p",
  184. observer, keyPath, selector, target];
  185. }
  186. return key;
  187. }
  188. - (void)addObserver:(id)observer
  189. ofObject:(id)target
  190. forKeyPath:(NSString *)keyPath
  191. selector:(SEL)selector
  192. userInfo:(id)userInfo
  193. options:(NSKeyValueObservingOptions)options {
  194. GTMKeyValueObservingHelper *helper
  195. = [[GTMKeyValueObservingHelper alloc] initWithObserver:observer
  196. object:target
  197. keyPath:keyPath
  198. selector:selector
  199. userInfo:userInfo
  200. options:options];
  201. id key = [self dictionaryKeyForObserver:observer
  202. ofObject:target
  203. forKeyPath:keyPath
  204. selector:selector];
  205. @synchronized(self) {
  206. GTMKeyValueObservingHelper *oldHelper = [observerHelpers_ objectForKey:key];
  207. if (oldHelper) {
  208. _GTMDevLog(@"%@ already observing %@ forKeyPath %@",
  209. observer, target, keyPath);
  210. [oldHelper deregister];
  211. }
  212. [observerHelpers_ setObject:helper forKey:key];
  213. }
  214. [helper release];
  215. }
  216. - (void)removeObserver:(id)observer
  217. ofObject:(id)target
  218. forKeyPath:(NSString *)keyPath
  219. selector:(SEL)selector {
  220. id key = [self dictionaryKeyForObserver:observer
  221. ofObject:target
  222. forKeyPath:keyPath
  223. selector:selector];
  224. NSMutableArray *allValidHelperKeys = [NSMutableArray array];
  225. NSArray *allValidHelpers = nil;
  226. @synchronized(self) {
  227. NSString *helperKey;
  228. GTM_FOREACH_OBJECT(helperKey, [observerHelpers_ allKeys]) {
  229. if ([helperKey hasPrefix:key]) {
  230. [allValidHelperKeys addObject:helperKey];
  231. }
  232. }
  233. #if DEBUG
  234. if ([allValidHelperKeys count] == 0) {
  235. _GTMDevLog(@"%@ was not observing %@ with keypath %@",
  236. observer, target, keyPath);
  237. }
  238. #endif // DEBUG
  239. allValidHelpers = [observerHelpers_ objectsForKeys:allValidHelperKeys
  240. notFoundMarker:[NSNull null]];
  241. [observerHelpers_ removeObjectsForKeys:allValidHelperKeys];
  242. }
  243. [allValidHelpers makeObjectsPerformSelector:@selector(deregister)];
  244. }
  245. @end
  246. @implementation NSObject (GTMKeyValueObservingAdditions)
  247. - (void)gtm_addObserver:(id)observer
  248. forKeyPath:(NSString *)keyPath
  249. selector:(SEL)selector
  250. userInfo:(id)userInfo
  251. options:(NSKeyValueObservingOptions)options {
  252. _GTMDevAssert(observer && [keyPath length] && selector,
  253. @"Missing observer, keyPath, or selector");
  254. GTMKeyValueObservingCenter *center
  255. = [GTMKeyValueObservingCenter defaultCenter];
  256. GTMAssertSelectorNilOrImplementedWithArguments(
  257. observer,
  258. selector,
  259. @encode(GTMKeyValueChangeNotification *),
  260. NULL);
  261. [center addObserver:observer
  262. ofObject:self
  263. forKeyPath:keyPath
  264. selector:selector
  265. userInfo:userInfo
  266. options:options];
  267. }
  268. - (void)gtm_removeObserver:(id)observer
  269. forKeyPath:(NSString *)keyPath
  270. selector:(SEL)selector {
  271. _GTMDevAssert(observer && [keyPath length] && selector,
  272. @"Missing observer, keyPath, or selector");
  273. GTMKeyValueObservingCenter *center
  274. = [GTMKeyValueObservingCenter defaultCenter];
  275. GTMAssertSelectorNilOrImplementedWithArguments(
  276. observer,
  277. selector,
  278. @encode(GTMKeyValueChangeNotification *),
  279. NULL);
  280. [center removeObserver:observer
  281. ofObject:self
  282. forKeyPath:keyPath
  283. selector:selector];
  284. }
  285. - (void)gtm_stopObservingAllKeyPaths {
  286. GTMKeyValueObservingCenter *center
  287. = [GTMKeyValueObservingCenter defaultCenter];
  288. [center removeObserver:self
  289. ofObject:nil
  290. forKeyPath:nil
  291. selector:Nil];
  292. }
  293. @end
  294. @implementation GTMKeyValueChangeNotification
  295. - (id)initWithKeyPath:(NSString *)keyPath ofObject:(id)object
  296. userInfo:(id)userInfo change:(NSDictionary *)change {
  297. if ((self = [super init])) {
  298. keyPath_ = [keyPath copy];
  299. object_ = [object retain];
  300. userInfo_ = [userInfo retain];
  301. change_ = [change retain];
  302. }
  303. return self;
  304. }
  305. - (void)dealloc {
  306. [keyPath_ release];
  307. [object_ release];
  308. [userInfo_ release];
  309. [change_ release];
  310. [super dealloc];
  311. }
  312. - (id)copyWithZone:(NSZone *)zone {
  313. return [[[self class] allocWithZone:zone] initWithKeyPath:keyPath_
  314. ofObject:object_
  315. userInfo:userInfo_
  316. change:change_];
  317. }
  318. - (BOOL)isEqual:(id)object {
  319. return ([keyPath_ isEqualToString:[object keyPath]]
  320. && [object_ isEqual:[object object]]
  321. && [userInfo_ isEqual:[object userInfo]]
  322. && [change_ isEqual:[object change]]);
  323. }
  324. - (NSString *)description {
  325. return [NSString stringWithFormat:
  326. @"%@ <object = %@ keypath = %@ userInfo = %@ change = %@>",
  327. [self class], object_, keyPath_, userInfo_, change_];
  328. }
  329. - (NSUInteger)hash {
  330. return [keyPath_ hash] + [object_ hash] + [userInfo_ hash] + [change_ hash];
  331. }
  332. - (NSString *)keyPath {
  333. return keyPath_;
  334. }
  335. - (id)object {
  336. return object_;
  337. }
  338. - (id)userInfo {
  339. return userInfo_;
  340. }
  341. - (NSDictionary *)change {
  342. return change_;
  343. }
  344. @end
  345. #ifdef DEBUG
  346. static void SwizzleMethodsInClass(Class cls, SEL sel1, SEL sel2) {
  347. Method m1 = class_getInstanceMethod(cls, sel1);
  348. Method m2 = class_getInstanceMethod(cls, sel2);
  349. method_exchangeImplementations(m1, m2);
  350. }
  351. #if GTM_PERFORM_KVO_CHECKS
  352. // This is only used when GTM_PERFORM_KVO_CHECKS is on.
  353. static void SwizzleClassMethodsInClass(Class cls, SEL sel1, SEL sel2) {
  354. Method m1 = class_getClassMethod(cls, sel1);
  355. Method m2 = class_getClassMethod(cls, sel2);
  356. method_exchangeImplementations(m1, m2);
  357. }
  358. #endif // GTM_PERFORM_KVO_CHECKS
  359. // This category exists to attempt to help deal with tricky KVO issues.
  360. // KVO is a wonderful technology in some ways, but is extremely fragile and
  361. // allows developers a lot of freedom to shoot themselves in the foot.
  362. // Refactoring an app that uses a lot of KVO can be really difficult, as can
  363. // debugging it.
  364. // These are some tools that we have found useful when working with KVO. Note
  365. // that these tools are only on in Debug builds.
  366. //
  367. // We have divided these tools up into two categories: Checks and Debugs.
  368. //
  369. // Debugs
  370. // Debugs are mainly for logging all the KVO/KVC that is occurring in your
  371. // application. To enable our KVO debugging, set the GTMDebugKVO environment
  372. // variable to 1 and you will get a whole pile of KVO logging that may help you
  373. // track down problems.
  374. // bash - export GTMDebugKVO=1
  375. // csh/tcsh - setenv GTMDebugKVO 1
  376. //
  377. // Checks
  378. // First we believe that instance variables should be private by default,
  379. // and that any KVO should be done via accessors. Apple by default allows KVO
  380. // to get at instance variables directly. Since our coding standards define
  381. // that instance variables should be @private, we feel that KVO shouldn't be
  382. // breaking this encapsulation. Unfortunately the @private, @protected
  383. // designators are a compile time convention only, and don't get carried over
  384. // into the runtime, so there's no way to check on an individual iVar what
  385. // it's visibility is. We therefore assume that an instance variable is private,
  386. // and disallow KVO access to instance variables. The problem with most KVO
  387. // issues is that they occur at runtime and unless you execute that case you
  388. // may never see the bug until it's too late. We try to force KVO issues to
  389. // rear their head at the time of the observing if at all possible.
  390. // Checks are on by default in debug builds. They can be turned off by defining
  391. // the compile flag GTM_PERFORM_KVO_CHECKS to 0
  392. // i.e. #define GTM_PERFORM_KVO_CHECKS 0, or set it
  393. // in GCC_PREPROCESSOR_DEFINITIONS.
  394. //
  395. // Checks work at a couple of different levels.
  396. // The most restrictive of the checks is that we set
  397. // |accessInstanceVariablesDirectly| to NO by default. This means that if you
  398. // attempt to perform KVO on an instance variable, you will get an exception
  399. // thrown.
  400. // Also, when adding an observer, we check to see if any member of the path
  401. // starts or ends with _ which by convention denotes an instance variable. If so
  402. // we warn you about attempting to access a ivar directly.
  403. @interface NSObject (GTMDebugKeyValueObserving)
  404. - (void)_gtmDebugAddObserver:(NSObject *)observer
  405. forKeyPath:(NSString *)keyPath
  406. options:(NSKeyValueObservingOptions)options
  407. context:(void *)context;
  408. - (void)_gtmDebugArrayAddObserver:(NSObject *)observer
  409. toObjectsAtIndexes:(NSIndexSet *)indexes
  410. forKeyPath:(NSString *)keyPath
  411. options:(NSKeyValueObservingOptions)options
  412. context:(void *)context;
  413. - (void)_gtmDebugRemoveObserver:(NSObject *)observer
  414. forKeyPath:(NSString *)keyPath;
  415. - (void)_gtmDebugArrayRemoveObserver:(NSObject *)observer
  416. fromObjectsAtIndexes:(NSIndexSet *)indexes
  417. forKeyPath:(NSString *)keyPath;
  418. - (void)_gtmDebugWillChangeValueForKey:(NSString*)key;
  419. - (void)_gtmDebugDidChangeValueForKey:(NSString*)key;
  420. #if GTM_PERFORM_KVO_CHECKS
  421. - (void)_gtmCheckAddObserver:(NSObject *)observer
  422. forKeyPath:(NSString *)keyPath
  423. options:(NSKeyValueObservingOptions)options
  424. context:(void *)context;
  425. - (void)_gtmCheckAddObserver:(NSObject *)observer
  426. toObjectsAtIndexes:(NSIndexSet *)indexes
  427. forKeyPath:(NSString *)keyPath
  428. options:(NSKeyValueObservingOptions)options
  429. context:(void *)context;
  430. + (BOOL)_gtmAccessInstanceVariablesDirectly;
  431. #endif // GTM_PERFORM_KVO_CHECKS
  432. @end
  433. @implementation NSObject (GTMDebugKeyValueObserving)
  434. GTM_METHOD_CHECK(NSObject, _gtmDebugAddObserver:forKeyPath:options:context:);
  435. GTM_METHOD_CHECK(NSObject, _gtmDebugRemoveObserver:forKeyPath:);
  436. GTM_METHOD_CHECK(NSObject, _gtmDebugWillChangeValueForKey:);
  437. GTM_METHOD_CHECK(NSObject, _gtmDebugDidChangeValueForKey:);
  438. GTM_METHOD_CHECK(NSArray,
  439. _gtmDebugArrayAddObserver:toObjectsAtIndexes:forKeyPath:options:context:);
  440. GTM_METHOD_CHECK(NSArray,
  441. _gtmDebugArrayRemoveObserver:fromObjectsAtIndexes:forKeyPath:);
  442. #if GTM_PERFORM_KVO_CHECKS
  443. GTM_METHOD_CHECK(NSObject,
  444. _gtmCheckAddObserver:forKeyPath:options:context:);
  445. GTM_METHOD_CHECK(NSArray,
  446. _gtmCheckAddObserver:toObjectsAtIndexes:forKeyPath:options:context:);
  447. GTM_METHOD_CHECK(NSObject,
  448. _gtmAccessInstanceVariablesDirectly);
  449. #endif // GTM_PERFORM_KVO_CHECKS
  450. + (void)load {
  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:));
  483. }
  484. #if GTM_PERFORM_KVO_CHECKS
  485. cls = [NSObject class];
  486. SwizzleMethodsInClass(cls,
  487. @selector(addObserver:forKeyPath:options:context:),
  488. @selector(_gtmCheckAddObserver:forKeyPath:options:context:));
  489. SwizzleClassMethodsInClass(cls,
  490. @selector(accessInstanceVariablesDirectly),
  491. @selector(_gtmAccessInstanceVariablesDirectly));
  492. cls = [NSArray class];
  493. SwizzleMethodsInClass(cls,
  494. @selector(addObserver:toObjectsAtIndexes:forKeyPath:options:context:),
  495. @selector(_gtmCheckAddObserver:toObjectsAtIndexes:forKeyPath:options:context:));
  496. #endif // GTM_PERFORM_KVO_CHECKS
  497. [pool drain];
  498. }
  499. - (void)_gtmDebugAddObserver:(NSObject *)observer
  500. forKeyPath:(NSString *)keyPath
  501. options:(NSKeyValueObservingOptions)options
  502. context:(void *)context {
  503. _GTMDevLog(@"Adding observer %@ to %@ keypath '%@'", observer, self, keyPath);
  504. [self _gtmDebugAddObserver:observer forKeyPath:keyPath
  505. options:options context:context];
  506. }
  507. - (void)_gtmDebugArrayAddObserver:(NSObject *)observer
  508. toObjectsAtIndexes:(NSIndexSet *)indexes
  509. forKeyPath:(NSString *)keyPath
  510. options:(NSKeyValueObservingOptions)options
  511. context:(void *)context {
  512. _GTMDevLog(@"Array adding observer %@ to indexes %@ of %@ keypath '%@'",
  513. observer, indexes, self, keyPath);
  514. [self _gtmDebugArrayAddObserver:observer
  515. toObjectsAtIndexes:indexes
  516. forKeyPath:keyPath
  517. options:options context:context];
  518. }
  519. - (void)_gtmDebugRemoveObserver:(NSObject *)observer
  520. forKeyPath:(NSString *)keyPath {
  521. _GTMDevLog(@"Removing observer %@ from %@ keypath '%@'",
  522. observer, self, keyPath);
  523. [self _gtmDebugRemoveObserver:observer forKeyPath:keyPath];
  524. }
  525. - (void)_gtmDebugArrayRemoveObserver:(NSObject *)observer
  526. fromObjectsAtIndexes:(NSIndexSet *)indexes
  527. forKeyPath:(NSString *)keyPath {
  528. _GTMDevLog(@"Array removing observer %@ from indexes %@ of %@ keypath '%@'",
  529. indexes, observer, self, keyPath);
  530. [self _gtmDebugArrayRemoveObserver:observer
  531. fromObjectsAtIndexes:indexes
  532. forKeyPath:keyPath];
  533. }
  534. - (void)_gtmDebugWillChangeValueForKey:(NSString*)key {
  535. _GTMDevLog(@"Will change '%@' of %@", key, self);
  536. [self _gtmDebugWillChangeValueForKey:key];
  537. }
  538. - (void)_gtmDebugDidChangeValueForKey:(NSString*)key {
  539. _GTMDevLog(@"Did change '%@' of %@", key, self);
  540. [self _gtmDebugDidChangeValueForKey:key];
  541. }
  542. #if GTM_PERFORM_KVO_CHECKS
  543. - (void)_gtmCheckAddObserver:(NSObject *)observer
  544. forKeyPath:(NSString *)keyPath
  545. options:(NSKeyValueObservingOptions)options
  546. context:(void *)context {
  547. NSArray *keyPathElements = [keyPath componentsSeparatedByString:@"."];
  548. NSString *element;
  549. GTM_FOREACH_OBJECT(element, keyPathElements) {
  550. if ([element hasPrefix:@"_"] || [element hasSuffix:@"_"]) {
  551. _GTMDevLog(@"warning: %@ is registering an observation on what appears "
  552. @"to be a private ivar of %@ (or a sub keyed object) with "
  553. @"element %@ of keyPath %@.", observer, self, element,
  554. keyPath);
  555. }
  556. }
  557. [self _gtmCheckAddObserver:observer
  558. forKeyPath:keyPath
  559. options:options
  560. context:context];
  561. }
  562. - (void)_gtmCheckAddObserver:(NSObject *)observer
  563. toObjectsAtIndexes:(NSIndexSet *)indexes
  564. forKeyPath:(NSString *)keyPath
  565. options:(NSKeyValueObservingOptions)options
  566. context:(void *)context {
  567. NSArray *keyPathElements = [keyPath componentsSeparatedByString:@"."];
  568. NSString *element;
  569. GTM_FOREACH_OBJECT(element, keyPathElements) {
  570. if ([element hasPrefix:@"_"] || [element hasSuffix:@"_"]) {
  571. _GTMDevLog(@"warning: %@ is registering an observation on what appears "
  572. @"to be a private ivar of %@ (or a sub keyed object) with "
  573. @"element %@ of keyPath %@.", observer, self, element,
  574. keyPath);
  575. }
  576. }
  577. [self _gtmCheckAddObserver:observer
  578. toObjectsAtIndexes:indexes
  579. forKeyPath:keyPath
  580. options:options
  581. context:context];
  582. }
  583. + (BOOL)_gtmAccessInstanceVariablesDirectly {
  584. // Apple has lots of "bad" direct instance variable accesses, so we
  585. // only want to check our code, as opposed to library code. iOS simulator
  586. // builds copy the app into the user's home directory. Xcode 4 also changes
  587. // the default location of the output directory. Don't count being within
  588. // the user's home and under "/Library/" as being a system library.
  589. // If this turns out to be slow, we may want to consider a cache to speed
  590. // things up.
  591. NSBundle *bundle = [NSBundle bundleForClass:self];
  592. NSString *path = [bundle bundlePath];
  593. BOOL hasLibrary = [path rangeOfString:@"/Library/"].location != NSNotFound;
  594. BOOL startsWithUser = [path hasPrefix:@"/Users/"];
  595. return !startsWithUser && hasLibrary;
  596. }
  597. #endif // GTM_PERFORM_KVO_CHECKS
  598. @end
  599. #endif // DEBUG