123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423 |
- ////////////////////////////////////////////////////////////////////////////
- //
- // Copyright 2016 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 "RLMCollection_Private.hpp"
- #import "RLMAccessor.hpp"
- #import "RLMArray_Private.hpp"
- #import "RLMListBase.h"
- #import "RLMObjectSchema_Private.hpp"
- #import "RLMObjectStore.h"
- #import "RLMObject_Private.hpp"
- #import "RLMProperty_Private.h"
- #import "collection_notifications.hpp"
- #import "list.hpp"
- #import "results.hpp"
- static const int RLMEnumerationBufferSize = 16;
- @implementation RLMFastEnumerator {
- // The buffer supplied by fast enumeration does not retain the objects given
- // to it, but because we create objects on-demand and don't want them
- // autoreleased (a table can have more rows than the device has memory for
- // accessor objects) we need a thing to retain them.
- id _strongBuffer[RLMEnumerationBufferSize];
- RLMRealm *_realm;
- RLMClassInfo *_info;
- // A pointer to either _snapshot or a Results from the source collection,
- // to avoid having to copy the Results when not in a write transaction
- realm::Results *_results;
- realm::Results _snapshot;
- // A strong reference to the collection being enumerated to ensure it stays
- // alive when we're holding a pointer to a member in it
- id _collection;
- }
- - (instancetype)initWithList:(realm::List&)list
- collection:(id)collection
- realm:(RLMRealm *)realm
- classInfo:(RLMClassInfo&)info
- {
- self = [super init];
- if (self) {
- if (realm.inWriteTransaction) {
- _snapshot = list.snapshot();
- }
- else {
- _snapshot = list.as_results();
- _collection = collection;
- [realm registerEnumerator:self];
- }
- _results = &_snapshot;
- _realm = realm;
- _info = &info;
- }
- return self;
- }
- - (instancetype)initWithResults:(realm::Results&)results
- collection:(id)collection
- realm:(RLMRealm *)realm
- classInfo:(RLMClassInfo&)info
- {
- self = [super init];
- if (self) {
- if (realm.inWriteTransaction) {
- _snapshot = results.snapshot();
- _results = &_snapshot;
- }
- else {
- _results = &results;
- _collection = collection;
- [realm registerEnumerator:self];
- }
- _realm = realm;
- _info = &info;
- }
- return self;
- }
- - (void)dealloc {
- if (_collection) {
- [_realm unregisterEnumerator:self];
- }
- }
- - (void)detach {
- _snapshot = _results->snapshot();
- _results = &_snapshot;
- _collection = nil;
- }
- - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
- count:(NSUInteger)len {
- [_realm verifyThread];
- if (!_results->is_valid()) {
- @throw RLMException(@"Collection is no longer valid");
- }
- // The fast enumeration buffer size is currently a hardcoded number in the
- // compiler so this can't actually happen, but just in case it changes in
- // the future...
- if (len > RLMEnumerationBufferSize) {
- len = RLMEnumerationBufferSize;
- }
- NSUInteger batchCount = 0, count = state->extra[1];
- @autoreleasepool {
- RLMAccessorContext ctx(_realm, *_info);
- for (NSUInteger index = state->state; index < count && batchCount < len; ++index) {
- _strongBuffer[batchCount] = _results->get(ctx, index);
- batchCount++;
- }
- }
- for (NSUInteger i = batchCount; i < len; ++i) {
- _strongBuffer[i] = nil;
- }
- if (batchCount == 0) {
- // Release our data if we're done, as we're autoreleased and so may
- // stick around for a while
- if (_collection) {
- _collection = nil;
- [_realm unregisterEnumerator:self];
- }
- _snapshot = {};
- }
- state->itemsPtr = (__unsafe_unretained id *)(void *)_strongBuffer;
- state->state += batchCount;
- state->mutationsPtr = state->extra+1;
- return batchCount;
- }
- @end
- NSUInteger RLMFastEnumerate(NSFastEnumerationState *state, NSUInteger len, id<RLMFastEnumerable> collection) {
- __autoreleasing RLMFastEnumerator *enumerator;
- if (state->state == 0) {
- enumerator = collection.fastEnumerator;
- state->extra[0] = (long)enumerator;
- state->extra[1] = collection.count;
- }
- else {
- enumerator = (__bridge id)(void *)state->extra[0];
- }
- return [enumerator countByEnumeratingWithState:state count:len];
- }
- template<typename Collection>
- NSArray *RLMCollectionValueForKey(Collection& collection, NSString *key,
- RLMRealm *realm, RLMClassInfo& info) {
- size_t count = collection.size();
- if (count == 0) {
- return @[];
- }
- NSMutableArray *array = [NSMutableArray arrayWithCapacity:count];
- if ([key isEqualToString:@"self"]) {
- RLMAccessorContext context(realm, info);
- for (size_t i = 0; i < count; ++i) {
- [array addObject:collection.get(context, i) ?: NSNull.null];
- }
- return array;
- }
- if (collection.get_type() != realm::PropertyType::Object) {
- RLMAccessorContext context(realm, info);
- for (size_t i = 0; i < count; ++i) {
- [array addObject:[collection.get(context, i) valueForKey:key] ?: NSNull.null];
- }
- return array;
- }
- RLMObject *accessor = RLMCreateManagedAccessor(info.rlmObjectSchema.accessorClass, realm, &info);
- // List properties need to be handled specially since we need to create a
- // new List each time
- if (info.rlmObjectSchema.isSwiftClass) {
- auto prop = info.rlmObjectSchema[key];
- if (prop && prop.array && prop.swiftIvar) {
- // Grab the actual class for the generic List from an instance of it
- // so that we can make instances of the List without creating a new
- // object accessor each time
- Class cls = [object_getIvar(accessor, prop.swiftIvar) class];
- RLMAccessorContext context(realm, info);
- for (size_t i = 0; i < count; ++i) {
- RLMListBase *list = [[cls alloc] init];
- list._rlmArray = [[RLMManagedArray alloc] initWithList:realm::List(realm->_realm, *info.table(),
- info.tableColumn(prop),
- collection.get(i).get_index())
- realm:realm parentInfo:&info
- property:prop];
- [array addObject:list];
- }
- return array;
- }
- }
- for (size_t i = 0; i < count; i++) {
- accessor->_row = collection.get(i);
- RLMInitializeSwiftAccessorGenerics(accessor);
- [array addObject:[accessor valueForKey:key] ?: NSNull.null];
- }
- return array;
- }
- template NSArray *RLMCollectionValueForKey(realm::Results&, NSString *, RLMRealm *, RLMClassInfo&);
- template NSArray *RLMCollectionValueForKey(realm::List&, NSString *, RLMRealm *, RLMClassInfo&);
- void RLMCollectionSetValueForKey(id<RLMFastEnumerable> collection, NSString *key, id value) {
- realm::TableView tv = [collection tableView];
- if (tv.size() == 0) {
- return;
- }
- RLMRealm *realm = collection.realm;
- RLMClassInfo *info = collection.objectInfo;
- RLMObject *accessor = RLMCreateManagedAccessor(info->rlmObjectSchema.accessorClass, realm, info);
- for (size_t i = 0; i < tv.size(); i++) {
- accessor->_row = tv[i];
- RLMInitializeSwiftAccessorGenerics(accessor);
- [accessor setValue:value forKey:key];
- }
- }
- NSString *RLMDescriptionWithMaxDepth(NSString *name,
- id<RLMCollection> collection,
- NSUInteger depth) {
- if (depth == 0) {
- return @"<Maximum depth exceeded>";
- }
- const NSUInteger maxObjects = 100;
- auto str = [NSMutableString stringWithFormat:@"%@<%@> <%p> (\n", name,
- [collection objectClassName] ?: RLMTypeToString([collection type]),
- (void *)collection];
- size_t index = 0, skipped = 0;
- for (id obj in collection) {
- NSString *sub;
- if ([obj respondsToSelector:@selector(descriptionWithMaxDepth:)]) {
- sub = [obj descriptionWithMaxDepth:depth - 1];
- }
- else {
- sub = [obj description];
- }
- // Indent child objects
- NSString *objDescription = [sub stringByReplacingOccurrencesOfString:@"\n"
- withString:@"\n\t"];
- [str appendFormat:@"\t[%zu] %@,\n", index++, objDescription];
- if (index >= maxObjects) {
- skipped = collection.count - maxObjects;
- break;
- }
- }
- // Remove last comma and newline characters
- if (collection.count > 0) {
- [str deleteCharactersInRange:NSMakeRange(str.length-2, 2)];
- }
- if (skipped) {
- [str appendFormat:@"\n\t... %zu objects skipped.", skipped];
- }
- [str appendFormat:@"\n)"];
- return str;
- }
- std::vector<std::pair<std::string, bool>> RLMSortDescriptorsToKeypathArray(NSArray<RLMSortDescriptor *> *properties) {
- std::vector<std::pair<std::string, bool>> keypaths;
- keypaths.reserve(properties.count);
- for (RLMSortDescriptor *desc in properties) {
- if ([desc.keyPath rangeOfString:@"@"].location != NSNotFound) {
- @throw RLMException(@"Cannot sort on key path '%@': KVC collection operators are not supported.", desc.keyPath);
- }
- keypaths.push_back({desc.keyPath.UTF8String, desc.ascending});
- }
- return keypaths;
- }
- @implementation RLMCancellationToken {
- realm::NotificationToken _token;
- __unsafe_unretained RLMRealm *_realm;
- }
- - (instancetype)initWithToken:(realm::NotificationToken)token realm:(RLMRealm *)realm {
- self = [super init];
- if (self) {
- _token = std::move(token);
- _realm = realm;
- }
- return self;
- }
- - (RLMRealm *)realm {
- return _realm;
- }
- - (void)suppressNextNotification {
- _token.suppress_next();
- }
- - (void)invalidate {
- _token = {};
- }
- @end
- @implementation RLMCollectionChange {
- realm::CollectionChangeSet _indices;
- }
- - (instancetype)initWithChanges:(realm::CollectionChangeSet)indices {
- self = [super init];
- if (self) {
- _indices = std::move(indices);
- }
- return self;
- }
- static NSArray *toArray(realm::IndexSet const& set) {
- NSMutableArray *ret = [NSMutableArray new];
- for (auto index : set.as_indexes()) {
- [ret addObject:@(index)];
- }
- return ret;
- }
- - (NSArray *)insertions {
- return toArray(_indices.insertions);
- }
- - (NSArray *)deletions {
- return toArray(_indices.deletions);
- }
- - (NSArray *)modifications {
- return toArray(_indices.modifications);
- }
- static NSArray *toIndexPathArray(realm::IndexSet const& set, NSUInteger section) {
- NSMutableArray *ret = [NSMutableArray new];
- NSUInteger path[2] = {section, 0};
- for (auto index : set.as_indexes()) {
- path[1] = index;
- [ret addObject:[NSIndexPath indexPathWithIndexes:path length:2]];
- }
- return ret;
- }
- - (NSArray<NSIndexPath *> *)deletionsInSection:(NSUInteger)section {
- return toIndexPathArray(_indices.deletions, section);
- }
- - (NSArray<NSIndexPath *> *)insertionsInSection:(NSUInteger)section {
- return toIndexPathArray(_indices.insertions, section);
- }
- - (NSArray<NSIndexPath *> *)modificationsInSection:(NSUInteger)section {
- return toIndexPathArray(_indices.modifications, section);
- }
- @end
- template<typename Collection>
- RLMNotificationToken *RLMAddNotificationBlock(id objcCollection,
- Collection& collection,
- void (^block)(id, RLMCollectionChange *, NSError *),
- bool suppressInitialChange) {
- auto skip = suppressInitialChange ? std::make_shared<bool>(true) : nullptr;
- auto cb = [=, &collection](realm::CollectionChangeSet const& changes,
- std::exception_ptr err) {
- if (err) {
- try {
- rethrow_exception(err);
- }
- catch (...) {
- NSError *error = nil;
- RLMRealmTranslateException(&error);
- block(nil, nil, error);
- return;
- }
- }
- if (skip && *skip) {
- *skip = false;
- block(objcCollection, nil, nil);
- }
- else if (changes.empty()) {
- block(objcCollection, nil, nil);
- }
- else {
- block(objcCollection, [[RLMCollectionChange alloc] initWithChanges:changes], nil);
- }
- };
- return [[RLMCancellationToken alloc] initWithToken:collection.add_notification_callback(cb)
- realm:(RLMRealm *)[objcCollection realm]];
- }
- // Explicitly instantiate the templated function for the two types we'll use it on
- template RLMNotificationToken *RLMAddNotificationBlock<realm::List>(id, realm::List&, void (^)(id, RLMCollectionChange *, NSError *), bool);
- template RLMNotificationToken *RLMAddNotificationBlock<realm::Results>(id, realm::Results&, void (^)(id, RLMCollectionChange *, NSError *), bool);
|