RLMObservation.mm 18 KB

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