RACKVOTrampoline.m 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. //
  2. // RACKVOTrampoline.m
  3. // ReactiveCocoa
  4. //
  5. // Created by Josh Abernathy on 1/15/13.
  6. // Copyright (c) 2013 GitHub, Inc. All rights reserved.
  7. //
  8. #import "RACKVOTrampoline.h"
  9. #import "NSObject+RACDeallocating.h"
  10. #import "RACCompoundDisposable.h"
  11. #import "RACKVOProxy.h"
  12. @interface RACKVOTrampoline ()
  13. // The keypath which the trampoline is observing.
  14. @property (nonatomic, readonly, copy) NSString *keyPath;
  15. // These properties should only be manipulated while synchronized on the
  16. // receiver.
  17. @property (nonatomic, readonly, copy) RACKVOBlock block;
  18. @property (nonatomic, readonly, unsafe_unretained) NSObject *unsafeTarget;
  19. @property (nonatomic, readonly, weak) NSObject *weakTarget;
  20. @property (nonatomic, readonly, weak) NSObject *observer;
  21. @end
  22. @implementation RACKVOTrampoline
  23. #pragma mark Lifecycle
  24. - (id)initWithTarget:(__weak NSObject *)target observer:(__weak NSObject *)observer keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(RACKVOBlock)block {
  25. NSCParameterAssert(keyPath != nil);
  26. NSCParameterAssert(block != nil);
  27. NSObject *strongTarget = target;
  28. if (strongTarget == nil) return nil;
  29. self = [super init];
  30. if (self == nil) return nil;
  31. _keyPath = [keyPath copy];
  32. _block = [block copy];
  33. _weakTarget = target;
  34. _unsafeTarget = strongTarget;
  35. _observer = observer;
  36. [RACKVOProxy.sharedProxy addObserver:self forContext:(__bridge void *)self];
  37. [strongTarget addObserver:RACKVOProxy.sharedProxy forKeyPath:self.keyPath options:options context:(__bridge void *)self];
  38. [strongTarget.rac_deallocDisposable addDisposable:self];
  39. [self.observer.rac_deallocDisposable addDisposable:self];
  40. return self;
  41. }
  42. - (void)dealloc {
  43. [self dispose];
  44. }
  45. #pragma mark Observation
  46. - (void)dispose {
  47. NSObject *target;
  48. NSObject *observer;
  49. @synchronized (self) {
  50. _block = nil;
  51. // The target should still exist at this point, because we still need to
  52. // tear down its KVO observation. Therefore, we can use the unsafe
  53. // reference (and need to, because the weak one will have been zeroed by
  54. // now).
  55. target = self.unsafeTarget;
  56. observer = self.observer;
  57. _unsafeTarget = nil;
  58. _observer = nil;
  59. }
  60. [target.rac_deallocDisposable removeDisposable:self];
  61. [observer.rac_deallocDisposable removeDisposable:self];
  62. [target removeObserver:RACKVOProxy.sharedProxy forKeyPath:self.keyPath context:(__bridge void *)self];
  63. [RACKVOProxy.sharedProxy removeObserver:self forContext:(__bridge void *)self];
  64. }
  65. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
  66. if (context != (__bridge void *)self) {
  67. [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
  68. return;
  69. }
  70. RACKVOBlock block;
  71. id observer;
  72. id target;
  73. @synchronized (self) {
  74. block = self.block;
  75. observer = self.observer;
  76. target = self.weakTarget;
  77. }
  78. if (block == nil || target == nil) return;
  79. block(target, observer, change);
  80. }
  81. @end