NSObject+RACPropertySubscribing.m 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. //
  2. // NSObject+RACPropertySubscribing.m
  3. // ReactiveObjC
  4. //
  5. // Created by Josh Abernathy on 3/2/12.
  6. // Copyright (c) 2012 GitHub, Inc. All rights reserved.
  7. //
  8. #import "NSObject+RACPropertySubscribing.h"
  9. #import <ReactiveObjC/RACEXTScope.h>
  10. #import "NSObject+RACDeallocating.h"
  11. #import "NSObject+RACDescription.h"
  12. #import "NSObject+RACKVOWrapper.h"
  13. #import "RACCompoundDisposable.h"
  14. #import "RACDisposable.h"
  15. #import "RACKVOTrampoline.h"
  16. #import "RACSubscriber.h"
  17. #import "RACSignal+Operations.h"
  18. #import "RACTuple.h"
  19. #import <libkern/OSAtomic.h>
  20. @implementation NSObject (RACPropertySubscribing)
  21. - (RACSignal *)rac_valuesForKeyPath:(NSString *)keyPath observer:(__weak NSObject *)observer {
  22. return [[[self
  23. rac_valuesAndChangesForKeyPath:keyPath options:NSKeyValueObservingOptionInitial observer:observer]
  24. map:^(RACTuple *value) {
  25. // -map: because it doesn't require the block trampoline that -reduceEach: uses
  26. return value[0];
  27. }]
  28. setNameWithFormat:@"RACObserve(%@, %@)", RACDescription(self), keyPath];
  29. }
  30. - (RACSignal *)rac_valuesAndChangesForKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options observer:(__weak NSObject *)weakObserver {
  31. NSObject *strongObserver = weakObserver;
  32. keyPath = [keyPath copy];
  33. NSRecursiveLock *objectLock = [[NSRecursiveLock alloc] init];
  34. objectLock.name = @"org.reactivecocoa.ReactiveObjC.NSObjectRACPropertySubscribing";
  35. __weak NSObject *weakSelf = self;
  36. RACSignal *deallocSignal = [[RACSignal
  37. zip:@[
  38. self.rac_willDeallocSignal,
  39. strongObserver.rac_willDeallocSignal ?: [RACSignal never]
  40. ]]
  41. doCompleted:^{
  42. // Forces deallocation to wait if the object variables are currently
  43. // being read on another thread.
  44. [objectLock lock];
  45. @onExit {
  46. [objectLock unlock];
  47. };
  48. }];
  49. return [[[RACSignal
  50. createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
  51. // Hold onto the lock the whole time we're setting up the KVO
  52. // observation, because any resurrection that might be caused by our
  53. // retaining below must be balanced out by the time -dealloc returns
  54. // (if another thread is waiting on the lock above).
  55. [objectLock lock];
  56. @onExit {
  57. [objectLock unlock];
  58. };
  59. __strong NSObject *observer __attribute__((objc_precise_lifetime)) = weakObserver;
  60. __strong NSObject *self __attribute__((objc_precise_lifetime)) = weakSelf;
  61. if (self == nil) {
  62. [subscriber sendCompleted];
  63. return nil;
  64. }
  65. return [self rac_observeKeyPath:keyPath options:options observer:observer block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {
  66. [subscriber sendNext:RACTuplePack(value, change)];
  67. }];
  68. }]
  69. takeUntil:deallocSignal]
  70. setNameWithFormat:@"%@ -rac_valueAndChangesForKeyPath: %@ options: %lu observer: %@", RACDescription(self), keyPath, (unsigned long)options, RACDescription(strongObserver)];
  71. }
  72. @end