123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- ////////////////////////////////////////////////////////////////////////////
- //
- // Copyright 2015 Realm Inc.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- //
- ////////////////////////////////////////////////////////////////////////////
- #import <Foundation/Foundation.h>
- #import "binding_context.hpp"
- #import <realm/row.hpp>
- #import <realm/table.hpp>
- #import <unordered_map>
- @class RLMObjectBase, RLMRealm, RLMSchema, RLMProperty, RLMObjectSchema;
- class RLMClassInfo;
- class RLMSchemaInfo;
- namespace realm {
- class History;
- class SharedGroup;
- }
- // RLMObservationInfo stores all of the KVO-related data for RLMObjectBase and
- // RLMArray. There is a one-to-one relationship between observed objects and
- // RLMObservationInfo instances, so it could be folded into RLMObjectBase, and
- // is a separate class mostly to avoid making all accessor objects far larger.
- //
- // RLMClassInfo stores a vector of pointers to the first observation info
- // created for each row. If there are multiple observation infos for a single
- // row (such as if there are multiple observed objects backed by a single row,
- // or if both an object and an array property of that object are observed),
- // they're stored in an intrusive doubly-linked-list in the `next` and `prev`
- // members. This is done primarily to make it simpler and faster to loop over
- // all of the observed objects for a single row, as that needs to be done for
- // every change.
- class RLMObservationInfo {
- public:
- RLMObservationInfo(id object);
- RLMObservationInfo(RLMClassInfo &objectSchema, std::size_t row, id object);
- ~RLMObservationInfo();
- realm::Row const& getRow() const {
- return row;
- }
- NSString *columnName(size_t col) const noexcept;
- // Send willChange/didChange notifications to all observers for this object/row
- // Sends the array versions if indexes is non-nil, normal versions otherwise
- void willChange(NSString *key, NSKeyValueChange kind=NSKeyValueChangeSetting, NSIndexSet *indexes=nil) const;
- void didChange(NSString *key, NSKeyValueChange kind=NSKeyValueChangeSetting, NSIndexSet *indexes=nil) const;
- bool isForRow(size_t ndx) const {
- return row && row.get_index() == ndx;
- }
- void recordObserver(realm::Row& row, RLMClassInfo *objectInfo, RLMObjectSchema *objectSchema, NSString *keyPath);
- void removeObserver();
- bool hasObservers() const { return observerCount > 0; }
- // valueForKey: on observed object and array properties needs to return the
- // same object each time for KVO to work at all. Doing this all the time
- // requires some odd semantics to avoid reference cycles, so instead we do
- // it only to the extent specifically required by KVO. In addition, we
- // need to continue to return the same object even if this row is deleted,
- // or deleting an object with active observers will explode horribly.
- // Once prepareForInvalidation() is called, valueForKey() will always return
- // the cached value for object and array properties without checking the
- // backing row to verify it's up-to-date.
- //
- // prepareForInvalidation() must be called on the head of the linked list
- // (i.e. on the object pointed to directly by the object schema)
- id valueForKey(NSString *key);
- void prepareForInvalidation();
- private:
- // Doubly-linked-list of observed objects for the same row as this
- RLMObservationInfo *next = nullptr;
- RLMObservationInfo *prev = nullptr;
- // Row being observed
- realm::Row row;
- RLMClassInfo *objectSchema = nullptr;
- // Object doing the observing
- __unsafe_unretained id object = nil;
- // valueForKey: hack
- bool invalidated = false;
- size_t observerCount = 0;
- NSString *lastKey = nil;
- __unsafe_unretained RLMProperty *lastProp = nil;
- // objects returned from valueForKey() to keep them alive in case observers
- // are added and so that they can still be accessed after row is detached
- NSMutableDictionary *cachedObjects;
- void setRow(realm::Table &table, size_t newRow);
- template<typename F>
- void forEach(F&& f) const {
- // The user's observation handler may release their last reference to
- // the object being observed, which will result in the RLMObservationInfo
- // being destroyed. As a result, we need to retain the object which owns
- // both `this` and the current info we're looking at.
- __attribute__((objc_precise_lifetime)) id self = object, current;
- for (auto info = prev; info; info = info->prev) {
- current = info->object;
- f(info->object);
- }
- for (auto info = this; info; info = info->next) {
- current = info->object;
- f(info->object);
- }
- }
- // Default move/copy constructors don't work due to the intrusive linked
- // list and we don't need them
- RLMObservationInfo(RLMObservationInfo const&) = delete;
- RLMObservationInfo(RLMObservationInfo&&) = delete;
- RLMObservationInfo& operator=(RLMObservationInfo const&) = delete;
- RLMObservationInfo& operator=(RLMObservationInfo&&) = delete;
- };
- // Get the the observation info chain for the given row
- // Will simply return info if it's non-null, and will search ojectSchema's array
- // for a matching one otherwise, and return null if there are none
- RLMObservationInfo *RLMGetObservationInfo(RLMObservationInfo *info, size_t row, RLMClassInfo& objectSchema);
- // delete all objects from a single table with change notifications
- void RLMClearTable(RLMClassInfo &realm);
- // invoke the block, sending notifications for cascading deletes/link nullifications
- void RLMTrackDeletions(RLMRealm *realm, dispatch_block_t block);
- std::vector<realm::BindingContext::ObserverState> RLMGetObservedRows(RLMSchemaInfo const& schema);
- void RLMWillChange(std::vector<realm::BindingContext::ObserverState> const& observed, std::vector<void *> const& invalidated);
- void RLMDidChange(std::vector<realm::BindingContext::ObserverState> const& observed, std::vector<void *> const& invalidated);
|