added iOS source code
[wl-app.git] / iOS / Pods / Realm / Realm / RLMObservation.mm
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 "RLMObservation.hpp"
20
21 #import "RLMAccessor.h"
22 #import "RLMArray_Private.hpp"
23 #import "RLMListBase.h"
24 #import "RLMObjectSchema_Private.hpp"
25 #import "RLMObject_Private.hpp"
26 #import "RLMProperty_Private.h"
27 #import "RLMRealm_Private.hpp"
28
29 #import <realm/group.hpp>
30
31 using namespace realm;
32
33 namespace {
34     template<typename Iterator>
35     struct IteratorPair {
36         Iterator first;
37         Iterator second;
38     };
39     template<typename Iterator>
40     Iterator begin(IteratorPair<Iterator> const& p) {
41         return p.first;
42     }
43     template<typename Iterator>
44     Iterator end(IteratorPair<Iterator> const& p) {
45         return p.second;
46     }
47
48     template<typename Container>
49     auto reverse(Container const& c) {
50         return IteratorPair<typename Container::const_reverse_iterator>{c.rbegin(), c.rend()};
51     }
52 }
53
54 RLMObservationInfo::RLMObservationInfo(RLMClassInfo &objectSchema, std::size_t row, id object)
55 : object(object)
56 , objectSchema(&objectSchema)
57 {
58     setRow(*objectSchema.table(), row);
59 }
60
61 RLMObservationInfo::RLMObservationInfo(id object)
62 : object(object)
63 {
64 }
65
66 RLMObservationInfo::~RLMObservationInfo() {
67     if (prev) {
68         // Not the head of the linked list, so just detach from the list
69         REALM_ASSERT_DEBUG(prev->next == this);
70         prev->next = next;
71         if (next) {
72             REALM_ASSERT_DEBUG(next->prev == this);
73             next->prev = prev;
74         }
75     }
76     else if (objectSchema) {
77         // The head of the list, so remove self from the object schema's array
78         // of observation info, either replacing self with the next info or
79         // removing entirely if there is no next
80         auto end = objectSchema->observedObjects.end();
81         auto it = find(objectSchema->observedObjects.begin(), end, this);
82         if (it != end) {
83             if (next) {
84                 *it = next;
85                 next->prev = nullptr;
86             }
87             else {
88                 iter_swap(it, std::prev(end));
89                 objectSchema->observedObjects.pop_back();
90             }
91         }
92     }
93     // Otherwise the observed object was unmanaged, so nothing to do
94
95 #ifdef DEBUG
96     // ensure that incorrect cleanup fails noisily
97     object = (__bridge id)(void *)-1;
98     prev = (RLMObservationInfo *)-1;
99     next = (RLMObservationInfo *)-1;
100 #endif
101 }
102
103 NSString *RLMObservationInfo::columnName(size_t col) const noexcept {
104     return objectSchema->propertyForTableColumn(col).name;
105 }
106
107 void RLMObservationInfo::willChange(NSString *key, NSKeyValueChange kind, NSIndexSet *indexes) const {
108     if (indexes) {
109         forEach([=](__unsafe_unretained auto o) {
110             [o willChange:kind valuesAtIndexes:indexes forKey:key];
111         });
112     }
113     else {
114         forEach([=](__unsafe_unretained auto o) {
115             [o willChangeValueForKey:key];
116         });
117     }
118 }
119
120 void RLMObservationInfo::didChange(NSString *key, NSKeyValueChange kind, NSIndexSet *indexes) const {
121     if (indexes) {
122         forEach([=](__unsafe_unretained auto o) {
123             [o didChange:kind valuesAtIndexes:indexes forKey:key];
124         });
125     }
126     else {
127         forEach([=](__unsafe_unretained auto o) {
128             [o didChangeValueForKey:key];
129         });
130     }
131 }
132
133 void RLMObservationInfo::prepareForInvalidation() {
134     REALM_ASSERT_DEBUG(objectSchema);
135     REALM_ASSERT_DEBUG(!prev);
136     for (auto info = this; info; info = info->next)
137         info->invalidated = true;
138 }
139
140 void RLMObservationInfo::setRow(realm::Table &table, size_t newRow) {
141     REALM_ASSERT_DEBUG(!row);
142     REALM_ASSERT_DEBUG(objectSchema);
143     row = table[newRow];
144     for (auto info : objectSchema->observedObjects) {
145         if (info->row && info->row.get_index() == row.get_index()) {
146             prev = info;
147             next = info->next;
148             if (next)
149                 next->prev = this;
150             info->next = this;
151             return;
152         }
153     }
154     objectSchema->observedObjects.push_back(this);
155 }
156
157 void RLMObservationInfo::recordObserver(realm::Row& objectRow, RLMClassInfo *objectInfo,
158                                         __unsafe_unretained RLMObjectSchema *const objectSchema,
159                                         __unsafe_unretained NSString *const keyPath) {
160     ++observerCount;
161     if (row) {
162         return;
163     }
164
165     // add ourselves to the list of observed objects if this is the first time
166     // an observer is being added to a managed object
167     if (objectRow) {
168         this->objectSchema = objectInfo;
169         setRow(*objectRow.get_table(), objectRow.get_index());
170         return;
171     }
172
173     // Arrays need a reference to their containing object to avoid having to
174     // go through the awful proxy object from mutableArrayValueForKey.
175     // For managed objects we do this when the object is added or created
176     // (and have to to support notifications from modifying an object which
177     // was never observed), but for Swift classes (both RealmSwift and
178     // RLMObject) we can't do it then because we don't know what the parent
179     // object is.
180
181     NSUInteger sep = [keyPath rangeOfString:@"."].location;
182     NSString *key = sep == NSNotFound ? keyPath : [keyPath substringToIndex:sep];
183     RLMProperty *prop = objectSchema[key];
184     if (prop && prop.array) {
185         id value = valueForKey(key);
186         RLMArray *array = [value isKindOfClass:[RLMListBase class]] ? [value _rlmArray] : value;
187         array->_key = key;
188         array->_parentObject = object;
189     }
190     else if (auto swiftIvar = prop.swiftIvar) {
191         if (auto optional = RLMDynamicCast<RLMOptionalBase>(object_getIvar(object, swiftIvar))) {
192             optional.property = prop;
193             optional.object = object;
194         }
195     }
196 }
197
198 void RLMObservationInfo::removeObserver() {
199     --observerCount;
200 }
201
202 id RLMObservationInfo::valueForKey(NSString *key) {
203     if (invalidated) {
204         if ([key isEqualToString:RLMInvalidatedKey]) {
205             return @YES;
206         }
207         return cachedObjects[key];
208     }
209
210     if (key != lastKey) {
211         lastKey = key;
212         lastProp = objectSchema ? objectSchema->rlmObjectSchema[key] : nil;
213     }
214
215     static auto superValueForKey = reinterpret_cast<id(*)(id, SEL, NSString *)>([NSObject methodForSelector:@selector(valueForKey:)]);
216     if (!lastProp) {
217         // Not a managed property, so use NSObject's implementation of valueForKey:
218         return RLMCoerceToNil(superValueForKey(object, @selector(valueForKey:), key));
219     }
220
221     auto getSuper = [&] {
222         return row ? RLMDynamicGet(object, lastProp) : RLMCoerceToNil(superValueForKey(object, @selector(valueForKey:), key));
223     };
224
225     // We need to return the same object each time for observing over keypaths
226     // to work, so we store a cache of them here. We can't just cache them on
227     // the object as that leads to retain cycles.
228     if (lastProp.array) {
229         RLMArray *value = cachedObjects[key];
230         if (!value) {
231             value = getSuper();
232             if (!cachedObjects) {
233                 cachedObjects = [NSMutableDictionary new];
234             }
235             cachedObjects[key] = value;
236         }
237         return value;
238     }
239
240     if (lastProp.type == RLMPropertyTypeObject) {
241         size_t col = row.get_column_index(lastProp.name.UTF8String);
242         if (row.is_null_link(col)) {
243             [cachedObjects removeObjectForKey:key];
244             return nil;
245         }
246
247         RLMObjectBase *value = cachedObjects[key];
248         if (value && value->_row.get_index() == row.get_link(col)) {
249             return value;
250         }
251         value = getSuper();
252         if (!cachedObjects) {
253             cachedObjects = [NSMutableDictionary new];
254         }
255         cachedObjects[key] = value;
256         return value;
257     }
258
259     return getSuper();
260 }
261
262 RLMObservationInfo *RLMGetObservationInfo(RLMObservationInfo *info, size_t row,
263                                           RLMClassInfo& objectSchema) {
264     if (info) {
265         return info;
266     }
267
268     for (RLMObservationInfo *info : objectSchema.observedObjects) {
269         if (info->isForRow(row)) {
270             return info;
271         }
272     }
273
274     return nullptr;
275 }
276
277 void RLMClearTable(RLMClassInfo &objectSchema) {
278     for (auto info : objectSchema.observedObjects) {
279         info->willChange(RLMInvalidatedKey);
280     }
281
282     RLMTrackDeletions(objectSchema.realm, ^{
283         objectSchema.table()->clear();
284
285         for (auto info : objectSchema.observedObjects) {
286             info->prepareForInvalidation();
287         }
288     });
289
290     for (auto info : reverse(objectSchema.observedObjects)) {
291         info->didChange(RLMInvalidatedKey);
292     }
293
294     objectSchema.observedObjects.clear();
295 }
296
297 void RLMTrackDeletions(__unsafe_unretained RLMRealm *const realm, dispatch_block_t block) {
298     std::vector<std::vector<RLMObservationInfo *> *> observers;
299
300     // Build up an array of observation info arrays which is indexed by table
301     // index (the object schemata may be in an entirely different order)
302     for (auto& info : realm->_info) {
303         if (info.second.observedObjects.empty()) {
304             continue;
305         }
306         size_t ndx = info.second.table()->get_index_in_group();
307         if (ndx >= observers.size()) {
308             observers.resize(std::max(observers.size() * 2, ndx + 1));
309         }
310         observers[ndx] = &info.second.observedObjects;
311     }
312
313     // No need for change tracking if no objects are observed
314     if (observers.empty()) {
315         block();
316         return;
317     }
318
319     struct change {
320         RLMObservationInfo *info;
321         __unsafe_unretained NSString *property;
322         NSMutableIndexSet *indexes;
323     };
324
325     std::vector<change> changes;
326     std::vector<RLMObservationInfo *> invalidated;
327
328     // This callback is called by core with a list of row deletions and
329     // resulting link nullifications immediately before things are deleted and nullified
330     realm.group.set_cascade_notification_handler([&](realm::Group::CascadeNotification const& cs) {
331         for (auto const& link : cs.links) {
332             size_t table_ndx = link.origin_table->get_index_in_group();
333             if (table_ndx >= observers.size() || !observers[table_ndx]) {
334                 // The modified table has no observers
335                 continue;
336             }
337
338             for (auto observer : *observers[table_ndx]) {
339                 if (!observer->isForRow(link.origin_row_ndx)) {
340                     continue;
341                 }
342
343                 NSString *name = observer->columnName(link.origin_col_ndx);
344                 if (observer->getRow().get_table()->get_column_type(link.origin_col_ndx) != type_LinkList) {
345                     changes.push_back({observer, name});
346                     continue;
347                 }
348
349                 auto c = find_if(begin(changes), end(changes), [&](auto const& c) {
350                     return c.info == observer && c.property == name;
351                 });
352                 if (c == end(changes)) {
353                     changes.push_back({observer, name, [NSMutableIndexSet new]});
354                     c = prev(end(changes));
355                 }
356
357                 // We know what row index is being removed from the LinkView,
358                 // but what we actually want is the indexes in the LinkView that
359                 // are going away
360                 auto linkview = observer->getRow().get_linklist(link.origin_col_ndx);
361                 size_t start = 0, index;
362                 while ((index = linkview->find(link.old_target_row_ndx, start)) != realm::not_found) {
363                     [c->indexes addIndex:index];
364                     start = index + 1;
365                 }
366             }
367         }
368
369         for (auto const& row : cs.rows) {
370             if (row.table_ndx >= observers.size() || !observers[row.table_ndx]) {
371                 // The modified table has no observers
372                 continue;
373             }
374
375             for (auto observer : *observers[row.table_ndx]) {
376                 if (observer->isForRow(row.row_ndx)) {
377                     invalidated.push_back(observer);
378                     break;
379                 }
380             }
381         }
382
383         // The relative order of these loops is very important
384         for (auto info : invalidated) {
385             info->willChange(RLMInvalidatedKey);
386         }
387         for (auto const& change : changes) {
388             change.info->willChange(change.property, NSKeyValueChangeRemoval, change.indexes);
389         }
390         for (auto info : invalidated) {
391             info->prepareForInvalidation();
392         }
393     });
394
395     try {
396         block();
397     }
398     catch (...) {
399         realm.group.set_cascade_notification_handler(nullptr);
400         throw;
401     }
402
403     for (auto const& change : reverse(changes)) {
404         change.info->didChange(change.property, NSKeyValueChangeRemoval, change.indexes);
405     }
406     for (auto info : reverse(invalidated)) {
407         info->didChange(RLMInvalidatedKey);
408     }
409
410     realm.group.set_cascade_notification_handler(nullptr);
411 }
412
413 namespace {
414 template<typename Func>
415 void forEach(realm::BindingContext::ObserverState const& state, Func&& func) {
416     for (size_t i = 0, size = state.changes.size(); i < size; ++i) {
417         if (state.changes[i].kind != realm::BindingContext::ColumnInfo::Kind::None) {
418             func(i, state.changes[i], static_cast<RLMObservationInfo *>(state.info));
419         }
420     }
421 }
422 }
423
424 std::vector<realm::BindingContext::ObserverState> RLMGetObservedRows(RLMSchemaInfo const& schema) {
425     std::vector<realm::BindingContext::ObserverState> observers;
426     for (auto& table : schema) {
427         for (auto info : table.second.observedObjects) {
428             auto const& row = info->getRow();
429             if (!row.is_attached())
430                 continue;
431             observers.push_back({
432                 row.get_table()->get_index_in_group(),
433                 row.get_index(),
434                 info});
435         }
436     }
437     sort(begin(observers), end(observers));
438     return observers;
439 }
440
441 static NSKeyValueChange convert(realm::BindingContext::ColumnInfo::Kind kind) {
442     switch (kind) {
443         case realm::BindingContext::ColumnInfo::Kind::None:
444         case realm::BindingContext::ColumnInfo::Kind::SetAll:
445             return NSKeyValueChangeSetting;
446         case realm::BindingContext::ColumnInfo::Kind::Set:
447             return NSKeyValueChangeReplacement;
448         case realm::BindingContext::ColumnInfo::Kind::Insert:
449             return NSKeyValueChangeInsertion;
450         case realm::BindingContext::ColumnInfo::Kind::Remove:
451             return NSKeyValueChangeRemoval;
452     }
453 }
454
455 static NSIndexSet *convert(realm::IndexSet const& in, NSMutableIndexSet *out) {
456     if (in.empty()) {
457         return nil;
458     }
459
460     [out removeAllIndexes];
461     for (auto range : in) {
462         [out addIndexesInRange:{range.first, range.second - range.first}];
463     }
464     return out;
465 }
466
467 void RLMWillChange(std::vector<realm::BindingContext::ObserverState> const& observed,
468                    std::vector<void *> const& invalidated) {
469     for (auto info : invalidated) {
470         static_cast<RLMObservationInfo *>(info)->willChange(RLMInvalidatedKey);
471     }
472     if (!observed.empty()) {
473         NSMutableIndexSet *indexes = [NSMutableIndexSet new];
474         for (auto const& o : observed) {
475             forEach(o, [&](size_t, auto const& change, RLMObservationInfo *info) {
476                 info->willChange(info->columnName(change.initial_column_index),
477                                  convert(change.kind), convert(change.indices, indexes));
478             });
479         }
480     }
481     for (auto info : invalidated) {
482         static_cast<RLMObservationInfo *>(info)->prepareForInvalidation();
483     }
484 }
485
486 void RLMDidChange(std::vector<realm::BindingContext::ObserverState> const& observed,
487                   std::vector<void *> const& invalidated) {
488     if (!observed.empty()) {
489         // Loop in reverse order to avoid O(N^2) behavior in Foundation
490         NSMutableIndexSet *indexes = [NSMutableIndexSet new];
491         for (auto const& o : reverse(observed)) {
492             forEach(o, [&](size_t i, auto const& change, RLMObservationInfo *info) {
493                 info->didChange(info->columnName(i), convert(change.kind), convert(change.indices, indexes));
494             });
495         }
496     }
497     for (auto const& info : reverse(invalidated)) {
498         static_cast<RLMObservationInfo *>(info)->didChange(RLMInvalidatedKey);
499     }
500 }