developer
2023-05-20 e12c7b4c22df631ebdcd16b2f98fbef8f738f92f
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
//
//  NSObject+RACPropertySubscribing.m
//  ReactiveCocoa
//
//  Created by Josh Abernathy on 3/2/12.
//  Copyright (c) 2012 GitHub, Inc. All rights reserved.
//
 
#import "NSObject+RACPropertySubscribing.h"
#import "RACEXTScope.h"
#import "NSObject+RACDeallocating.h"
#import "NSObject+RACDescription.h"
#import "NSObject+RACKVOWrapper.h"
#import "RACCompoundDisposable.h"
#import "RACDisposable.h"
#import "RACKVOTrampoline.h"
#import "RACSubscriber.h"
#import "RACSignal+Operations.h"
#import "RACTuple.h"
#import <libkern/OSAtomic.h>
 
@implementation NSObject (RACPropertySubscribing)
 
- (RACSignal *)rac_valuesForKeyPath:(NSString *)keyPath observer:(__weak NSObject *)observer {
    return [[[self
        rac_valuesAndChangesForKeyPath:keyPath options:NSKeyValueObservingOptionInitial observer:observer]
        map:^(RACTuple *value) {
            // -map: because it doesn't require the block trampoline that -reduceEach: uses
            return value[0];
        }]
        setNameWithFormat:@"RACObserve(%@, %@)", self.rac_description, keyPath];
}
 
- (RACSignal *)rac_valuesAndChangesForKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options observer:(__weak NSObject *)weakObserver {
    NSObject *strongObserver = weakObserver;
    keyPath = [keyPath copy];
 
    NSRecursiveLock *objectLock = [[NSRecursiveLock alloc] init];
    objectLock.name = @"org.reactivecocoa.ReactiveCocoa.NSObjectRACPropertySubscribing";
 
    __weak NSObject *weakSelf = self;
 
    RACSignal *deallocSignal = [[RACSignal
        zip:@[
            self.rac_willDeallocSignal,
            strongObserver.rac_willDeallocSignal ?: [RACSignal never]
        ]]
        doCompleted:^{
            // Forces deallocation to wait if the object variables are currently
            // being read on another thread.
            [objectLock lock];
            @onExit {
                [objectLock unlock];
            };
        }];
 
    return [[[RACSignal
        createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
            // Hold onto the lock the whole time we're setting up the KVO
            // observation, because any resurrection that might be caused by our
            // retaining below must be balanced out by the time -dealloc returns
            // (if another thread is waiting on the lock above).
            [objectLock lock];
            @onExit {
                [objectLock unlock];
            };
 
            __strong NSObject *observer __attribute__((objc_precise_lifetime)) = weakObserver;
            __strong NSObject *self __attribute__((objc_precise_lifetime)) = weakSelf;
 
            if (self == nil) {
                [subscriber sendCompleted];
                return nil;
            }
 
            return [self rac_observeKeyPath:keyPath options:options observer:observer block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {
                [subscriber sendNext:RACTuplePack(value, change)];
            }];
        }]
        takeUntil:deallocSignal]
        setNameWithFormat:@"%@ -rac_valueAndChangesForKeyPath: %@ options: %lu observer: %@", self.rac_description, keyPath, (unsigned long)options, strongObserver.rac_description];
}
 
@end
 
static RACSignal *signalWithoutChangesFor(Class class, NSObject *object, NSString *keyPath, NSKeyValueObservingOptions options, NSObject *observer) {
    NSCParameterAssert(object != nil);
    NSCParameterAssert(keyPath != nil);
    NSCParameterAssert(observer != nil);
 
    keyPath = [keyPath copy];
 
    @unsafeify(object);
 
    return [[class
        rac_signalWithChangesFor:object keyPath:keyPath options:options observer:observer]
        map:^(NSDictionary *change) {
            @strongify(object);
            return [object valueForKeyPath:keyPath];
        }];
}
 
@implementation NSObject (RACPropertySubscribingDeprecated)
 
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-implementations"
 
+ (RACSignal *)rac_signalFor:(NSObject *)object keyPath:(NSString *)keyPath observer:(NSObject *)observer {
    return signalWithoutChangesFor(self, object, keyPath, 0, observer);
}
 
+ (RACSignal *)rac_signalWithStartingValueFor:(NSObject *)object keyPath:(NSString *)keyPath observer:(NSObject *)observer {
    return signalWithoutChangesFor(self, object, keyPath, NSKeyValueObservingOptionInitial, observer);
}
 
+ (RACSignal *)rac_signalWithChangesFor:(NSObject *)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options observer:(NSObject *)observer {
    @unsafeify(observer, object);
    return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
 
        @strongify(observer, object);
        RACKVOTrampoline *KVOTrampoline = [object rac_addObserver:observer forKeyPath:keyPath options:options block:^(id target, id observer, NSDictionary *change) {
            [subscriber sendNext:change];
        }];
 
        @weakify(subscriber);
        RACDisposable *deallocDisposable = [RACDisposable disposableWithBlock:^{
            @strongify(subscriber);
            [KVOTrampoline dispose];
            [subscriber sendCompleted];
        }];
 
        [observer.rac_deallocDisposable addDisposable:deallocDisposable];
        [object.rac_deallocDisposable addDisposable:deallocDisposable];
 
        RACCompoundDisposable *observerDisposable = observer.rac_deallocDisposable;
        RACCompoundDisposable *objectDisposable = object.rac_deallocDisposable;
        return [RACDisposable disposableWithBlock:^{
            [observerDisposable removeDisposable:deallocDisposable];
            [objectDisposable removeDisposable:deallocDisposable];
            [KVOTrampoline dispose];
        }];
    }] setNameWithFormat:@"RACAble(%@, %@)", object.rac_description, keyPath];
}
 
- (RACSignal *)rac_signalForKeyPath:(NSString *)keyPath observer:(NSObject *)observer {
    return [self.class rac_signalFor:self keyPath:keyPath observer:observer];
}
 
- (RACSignal *)rac_signalWithStartingValueForKeyPath:(NSString *)keyPath observer:(NSObject *)observer {
    return [self.class rac_signalWithStartingValueFor:self keyPath:keyPath observer:observer];
}
 
- (RACDisposable *)rac_deriveProperty:(NSString *)keyPath from:(RACSignal *)signal {
    return [signal setKeyPath:keyPath onObject:self];
}
 
#pragma clang diagnostic pop
 
@end