added iOS source code
[wl-app.git] / iOS / Pods / Realm / include / RLMObservation.hpp
1 ////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright 2015 Realm Inc.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 ////////////////////////////////////////////////////////////////////////////
18
19 #import <Foundation/Foundation.h>
20
21 #import "binding_context.hpp"
22
23 #import <realm/row.hpp>
24 #import <realm/table.hpp>
25
26 #import <unordered_map>
27
28 @class RLMObjectBase, RLMRealm, RLMSchema, RLMProperty, RLMObjectSchema;
29 class RLMClassInfo;
30 class RLMSchemaInfo;
31
32 namespace realm {
33     class History;
34     class SharedGroup;
35 }
36
37 // RLMObservationInfo stores all of the KVO-related data for RLMObjectBase and
38 // RLMArray. There is a one-to-one relationship between observed objects and
39 // RLMObservationInfo instances, so it could be folded into RLMObjectBase, and
40 // is a separate class mostly to avoid making all accessor objects far larger.
41 //
42 // RLMClassInfo stores a vector of pointers to the first observation info
43 // created for each row. If there are multiple observation infos for a single
44 // row (such as if there are multiple observed objects backed by a single row,
45 // or if both an object and an array property of that object are observed),
46 // they're stored in an intrusive doubly-linked-list in the `next` and `prev`
47 // members. This is done primarily to make it simpler and faster to loop over
48 // all of the observed objects for a single row, as that needs to be done for
49 // every change.
50 class RLMObservationInfo {
51 public:
52     RLMObservationInfo(id object);
53     RLMObservationInfo(RLMClassInfo &objectSchema, std::size_t row, id object);
54     ~RLMObservationInfo();
55
56     realm::Row const& getRow() const {
57         return row;
58     }
59
60     NSString *columnName(size_t col) const noexcept;
61
62     // Send willChange/didChange notifications to all observers for this object/row
63     // Sends the array versions if indexes is non-nil, normal versions otherwise
64     void willChange(NSString *key, NSKeyValueChange kind=NSKeyValueChangeSetting, NSIndexSet *indexes=nil) const;
65     void didChange(NSString *key, NSKeyValueChange kind=NSKeyValueChangeSetting, NSIndexSet *indexes=nil) const;
66
67     bool isForRow(size_t ndx) const {
68         return row && row.get_index() == ndx;
69     }
70
71     void recordObserver(realm::Row& row, RLMClassInfo *objectInfo, RLMObjectSchema *objectSchema, NSString *keyPath);
72     void removeObserver();
73     bool hasObservers() const { return observerCount > 0; }
74
75     // valueForKey: on observed object and array properties needs to return the
76     // same object each time for KVO to work at all. Doing this all the time
77     // requires some odd semantics to avoid reference cycles, so instead we do
78     // it only to the extent specifically required by KVO. In addition, we
79     // need to continue to return the same object even if this row is deleted,
80     // or deleting an object with active observers will explode horribly.
81     // Once prepareForInvalidation() is called, valueForKey() will always return
82     // the cached value for object and array properties without checking the
83     // backing row to verify it's up-to-date.
84     //
85     // prepareForInvalidation() must be called on the head of the linked list
86     // (i.e. on the object pointed to directly by the object schema)
87     id valueForKey(NSString *key);
88
89     void prepareForInvalidation();
90
91 private:
92     // Doubly-linked-list of observed objects for the same row as this
93     RLMObservationInfo *next = nullptr;
94     RLMObservationInfo *prev = nullptr;
95
96     // Row being observed
97     realm::Row row;
98     RLMClassInfo *objectSchema = nullptr;
99
100     // Object doing the observing
101     __unsafe_unretained id object = nil;
102
103     // valueForKey: hack
104     bool invalidated = false;
105     size_t observerCount = 0;
106     NSString *lastKey = nil;
107     __unsafe_unretained RLMProperty *lastProp = nil;
108
109     // objects returned from valueForKey() to keep them alive in case observers
110     // are added and so that they can still be accessed after row is detached
111     NSMutableDictionary *cachedObjects;
112
113     void setRow(realm::Table &table, size_t newRow);
114
115     template<typename F>
116     void forEach(F&& f) const {
117         // The user's observation handler may release their last reference to
118         // the object being observed, which will result in the RLMObservationInfo
119         // being destroyed. As a result, we need to retain the object which owns
120         // both `this` and the current info we're looking at.
121         __attribute__((objc_precise_lifetime)) id self = object, current;
122         for (auto info = prev; info; info = info->prev) {
123             current = info->object;
124             f(info->object);
125         }
126         for (auto info = this; info; info = info->next) {
127             current = info->object;
128             f(info->object);
129         }
130     }
131
132     // Default move/copy constructors don't work due to the intrusive linked
133     // list and we don't need them
134     RLMObservationInfo(RLMObservationInfo const&) = delete;
135     RLMObservationInfo(RLMObservationInfo&&) = delete;
136     RLMObservationInfo& operator=(RLMObservationInfo const&) = delete;
137     RLMObservationInfo& operator=(RLMObservationInfo&&) = delete;
138 };
139
140 // Get the the observation info chain for the given row
141 // Will simply return info if it's non-null, and will search ojectSchema's array
142 // for a matching one otherwise, and return null if there are none
143 RLMObservationInfo *RLMGetObservationInfo(RLMObservationInfo *info, size_t row, RLMClassInfo& objectSchema);
144
145 // delete all objects from a single table with change notifications
146 void RLMClearTable(RLMClassInfo &realm);
147
148 // invoke the block, sending notifications for cascading deletes/link nullifications
149 void RLMTrackDeletions(RLMRealm *realm, dispatch_block_t block);
150
151 std::vector<realm::BindingContext::ObserverState> RLMGetObservedRows(RLMSchemaInfo const& schema);
152 void RLMWillChange(std::vector<realm::BindingContext::ObserverState> const& observed, std::vector<void *> const& invalidated);
153 void RLMDidChange(std::vector<realm::BindingContext::ObserverState> const& observed, std::vector<void *> const& invalidated);