1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099 |
- ////////////////////////////////////////////////////////////////////////////
- //
- // Copyright 2014 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 "RLMRealm_Private.hpp"
- #import "RLMAnalytics.hpp"
- #import "RLMAsyncTask_Private.h"
- #import "RLMArray_Private.hpp"
- #import "RLMDictionary_Private.hpp"
- #import "RLMError_Private.hpp"
- #import "RLMLogger.h"
- #import "RLMMigration_Private.h"
- #import "RLMObject_Private.h"
- #import "RLMObject_Private.hpp"
- #import "RLMObjectSchema_Private.hpp"
- #import "RLMObjectStore.h"
- #import "RLMObservation.hpp"
- #import "RLMProperty.h"
- #import "RLMProperty_Private.h"
- #import "RLMQueryUtil.hpp"
- #import "RLMRealmConfiguration_Private.hpp"
- #import "RLMRealmUtil.hpp"
- #import "RLMScheduler.h"
- #import "RLMSchema_Private.hpp"
- #import "RLMSyncConfiguration.h"
- #import "RLMSyncConfiguration_Private.hpp"
- #import "RLMSet_Private.hpp"
- #import "RLMThreadSafeReference_Private.hpp"
- #import "RLMUpdateChecker.hpp"
- #import "RLMUtil.hpp"
- #import <realm/disable_sync_to_disk.hpp>
- #import <realm/object-store/impl/realm_coordinator.hpp>
- #import <realm/object-store/object_store.hpp>
- #import <realm/object-store/schema.hpp>
- #import <realm/object-store/shared_realm.hpp>
- #import <realm/object-store/util/scheduler.hpp>
- #import <realm/util/scope_exit.hpp>
- #import <realm/version.hpp>
- #if REALM_ENABLE_SYNC
- #import "RLMSyncManager_Private.hpp"
- #import "RLMSyncSession_Private.hpp"
- #import "RLMSyncUtil_Private.hpp"
- #import "RLMSyncSubscription_Private.hpp"
- #import <realm/object-store/sync/sync_session.hpp>
- #endif
- using namespace realm;
- using util::File;
- @interface RLMRealmNotificationToken : RLMNotificationToken
- @property (nonatomic, strong) RLMRealm *realm;
- @property (nonatomic, copy) RLMNotificationBlock block;
- @end
- @interface RLMRealm ()
- @property (nonatomic, strong) NSHashTable<RLMRealmNotificationToken *> *notificationHandlers;
- - (void)sendNotifications:(RLMNotification)notification;
- @end
- void RLMDisableSyncToDisk() {
- realm::disable_sync_to_disk();
- }
- static std::atomic<bool> s_set_skip_backup_attribute{true};
- void RLMSetSkipBackupAttribute(bool value) {
- s_set_skip_backup_attribute = value;
- }
- static void RLMAddSkipBackupAttributeToItemAtPath(std::string_view path) {
- [[NSURL fileURLWithPath:@(path.data())] setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:nil];
- }
- void RLMWaitForRealmToClose(NSString *path) {
- NSString *lockfilePath = [path stringByAppendingString:@".lock"];
- File lockfile(lockfilePath.UTF8String, File::mode_Update);
- lockfile.set_fifo_path([path stringByAppendingString:@".management"].UTF8String, "lock.fifo");
- while (!lockfile.try_rw_lock_exclusive()) {
- sched_yield();
- }
- }
- BOOL RLMIsRealmCachedAtPath(NSString *path) {
- return RLMGetAnyCachedRealmForPath([path cStringUsingEncoding:NSUTF8StringEncoding]) != nil;
- }
- RLM_HIDDEN
- @implementation RLMRealmNotificationToken
- - (bool)invalidate {
- if (_realm) {
- [_realm verifyThread];
- [_realm.notificationHandlers removeObject:self];
- _realm = nil;
- _block = nil;
- return true;
- }
- return false;
- }
- - (void)suppressNextNotification {
- // Temporarily replace the block with one which restores the old block
- // rather than producing a notification.
- // This briefly creates a retain cycle but it's fine because the block will
- // be synchronously called shortly after this method is called. Unlike with
- // collection notifications, this does not have to go through the object
- // store or do fancy things to handle transaction coalescing because it's
- // called synchronously by the obj-c code and not by the object store.
- auto notificationBlock = _block;
- _block = ^(RLMNotification, RLMRealm *) {
- _block = notificationBlock;
- };
- }
- - (void)dealloc {
- if (_realm || _block) {
- NSLog(@"RLMNotificationToken released without unregistering a notification. You must hold "
- @"on to the RLMNotificationToken returned from addNotificationBlock and call "
- @"-[RLMNotificationToken invalidate] when you no longer wish to receive RLMRealm notifications.");
- }
- }
- @end
- static bool shouldForciblyDisableEncryption() {
- static bool disableEncryption = getenv("REALM_DISABLE_ENCRYPTION");
- return disableEncryption;
- }
- NSData *RLMRealmValidatedEncryptionKey(NSData *key) {
- if (shouldForciblyDisableEncryption()) {
- return nil;
- }
- if (key && key.length != 64) {
- @throw RLMException(@"Encryption key must be exactly 64 bytes long");
- }
- return key;
- }
- REALM_NOINLINE void RLMRealmTranslateException(NSError **error) {
- try {
- throw;
- }
- catch (FileAccessError const& ex) {
- RLMSetErrorOrThrow(makeError(ex), error);
- }
- catch (Exception const& ex) {
- RLMSetErrorOrThrow(makeError(ex), error);
- }
- catch (std::system_error const& ex) {
- RLMSetErrorOrThrow(makeError(ex), error);
- }
- catch (std::exception const& ex) {
- RLMSetErrorOrThrow(makeError(ex), error);
- }
- }
- namespace {
- // ARC tries to eliminate calls to autorelease when the value is then immediately
- // returned, but this results in significantly different semantics between debug
- // and release builds for RLMRealm, so force it to always autorelease.
- // NEXT-MAJOR: we should switch to NS_RETURNS_RETAINED, which did not exist yet
- // when we wrote this but is the correct thing.
- id autorelease(__unsafe_unretained id value) {
- // +1 __bridge_retained, -1 CFAutorelease
- return value ? (__bridge id)CFAutorelease((__bridge_retained CFTypeRef)value) : nil;
- }
- RLMRealm *getCachedRealm(RLMRealmConfiguration *configuration, RLMScheduler *options) NS_RETURNS_RETAINED {
- auto& config = configuration.configRef;
- if (!configuration.cache && !configuration.dynamic) {
- return nil;
- }
- RLMRealm *realm = RLMGetCachedRealm(configuration, options);
- if (!realm) {
- return nil;
- }
- auto const& oldConfig = realm->_realm->config();
- if ((oldConfig.read_only() || oldConfig.immutable()) != configuration.readOnly) {
- @throw RLMException(@"Realm at path '%@' already opened with different read permissions", configuration.fileURL.path);
- }
- if (oldConfig.in_memory != config.in_memory) {
- @throw RLMException(@"Realm at path '%@' already opened with different inMemory settings", configuration.fileURL.path);
- }
- if (realm.dynamic != configuration.dynamic) {
- @throw RLMException(@"Realm at path '%@' already opened with different dynamic settings", configuration.fileURL.path);
- }
- if (oldConfig.encryption_key != config.encryption_key) {
- @throw RLMException(@"Realm at path '%@' already opened with different encryption key", configuration.fileURL.path);
- }
- return autorelease(realm);
- }
- bool copySeedFile(RLMRealmConfiguration *configuration, NSError **error) {
- if (!configuration.seedFilePath) {
- return false;
- }
- @autoreleasepool {
- bool didCopySeed = false;
- NSError *copyError;
- DB::call_with_lock(configuration.path, [&](auto const&) {
- didCopySeed = [[NSFileManager defaultManager] copyItemAtURL:configuration.seedFilePath
- toURL:configuration.fileURL
- error:©Error];
- });
- if (!didCopySeed && copyError != nil && copyError.code != NSFileWriteFileExistsError) {
- RLMSetErrorOrThrow(copyError, error);
- return true;
- }
- }
- return false;
- }
- } // anonymous namespace
- @implementation RLMRealm {
- std::mutex _collectionEnumeratorMutex;
- NSHashTable<RLMFastEnumerator *> *_collectionEnumerators;
- bool _sendingNotifications;
- }
- + (void)initialize {
- static bool initialized;
- if (initialized) {
- return;
- }
- initialized = true;
- RLMCheckForUpdates();
- RLMSendAnalytics();
- // In cases where we are not using a synced Realm, we initialise the default logger
- // before opening any realm.
- [RLMLogger class];
- }
- - (instancetype)initPrivate {
- self = [super init];
- return self;
- }
- - (BOOL)isEmpty {
- return realm::ObjectStore::is_empty(self.group);
- }
- - (void)verifyThread {
- try {
- _realm->verify_thread();
- }
- catch (std::exception const& e) {
- @throw RLMException(e);
- }
- }
- - (BOOL)inWriteTransaction {
- return _realm->is_in_transaction();
- }
- - (realm::Group &)group {
- return _realm->read_group();
- }
- - (BOOL)autorefresh {
- return _realm->auto_refresh();
- }
- - (void)setAutorefresh:(BOOL)autorefresh {
- try {
- _realm->set_auto_refresh(autorefresh);
- }
- catch (std::exception const& e) {
- @throw RLMException(e);
- }
- }
- + (instancetype)defaultRealm {
- return [RLMRealm realmWithConfiguration:[RLMRealmConfiguration rawDefaultConfiguration] error:nil];
- }
- + (instancetype)defaultRealmForQueue:(dispatch_queue_t)queue {
- return [RLMRealm realmWithConfiguration:[RLMRealmConfiguration rawDefaultConfiguration]
- queue:queue error:nil];
- }
- + (instancetype)realmWithURL:(NSURL *)fileURL {
- RLMRealmConfiguration *configuration = [RLMRealmConfiguration defaultConfiguration];
- configuration.fileURL = fileURL;
- return [RLMRealm realmWithConfiguration:configuration error:nil];
- }
- + (RLMAsyncOpenTask *)asyncOpenWithConfiguration:(RLMRealmConfiguration *)configuration
- callbackQueue:(dispatch_queue_t)callbackQueue
- callback:(RLMAsyncOpenRealmCallback)callback {
- return [[RLMAsyncOpenTask alloc] initWithConfiguration:configuration
- confinedTo:[RLMScheduler dispatchQueue:callbackQueue]
- download:true completion:callback];
- }
- + (instancetype)realmWithSharedRealm:(SharedRealm)sharedRealm
- schema:(RLMSchema *)schema
- dynamic:(bool)dynamic {
- RLMRealm *realm = [[RLMRealm alloc] initPrivate];
- realm->_realm = sharedRealm;
- realm->_dynamic = dynamic;
- realm->_schema = schema;
- if (!dynamic) {
- realm->_realm->set_schema_subset(schema.objectStoreCopy);
- }
- realm->_info = RLMSchemaInfo(realm);
- return autorelease(realm);
- }
- + (instancetype)realmWithConfiguration:(RLMRealmConfiguration *)configuration error:(NSError **)error {
- return autorelease([self realmWithConfiguration:configuration
- confinedTo:RLMScheduler.currentRunLoop
- error:error]);
- }
- + (instancetype)realmWithConfiguration:(RLMRealmConfiguration *)configuration
- queue:(dispatch_queue_t)queue
- error:(NSError **)error {
- return autorelease([self realmWithConfiguration:configuration
- confinedTo:[RLMScheduler dispatchQueue:queue]
- error:error]);
- }
- + (instancetype)realmWithConfiguration:(RLMRealmConfiguration *)configuration
- confinedTo:(RLMScheduler *)scheduler
- error:(NSError **)error {
- // First check if we already have a cached Realm for this config
- if (auto realm = getCachedRealm(configuration, scheduler)) {
- return realm;
- }
- if (copySeedFile(configuration, error)) {
- return nil;
- }
- bool dynamic = configuration.dynamic;
- bool cache = configuration.cache;
- Realm::Config config = configuration.config;
- RLMRealm *realm = [[self alloc] initPrivate];
- realm->_dynamic = dynamic;
- realm->_actor = scheduler.actor;
- // protects the realm cache and accessors cache
- static auto& initLock = *new RLMUnfairMutex;
- std::lock_guard lock(initLock);
- try {
- config.scheduler = scheduler.osScheduler;
- if (config.scheduler && !config.scheduler->is_on_thread()) {
- throw RLMException(@"Realm opened from incorrect dispatch queue.");
- }
- realm->_realm = Realm::get_shared_realm(config);
- }
- catch (...) {
- RLMRealmTranslateException(error);
- return nil;
- }
- bool realmIsCached = false;
- // if we have a cached realm on another thread we can skip a few steps and
- // just grab its schema
- @autoreleasepool {
- // ensure that cachedRealm doesn't end up in this thread's autorelease pool
- if (auto cachedRealm = RLMGetAnyCachedRealmForPath(config.path)) {
- realm->_realm->set_schema_subset(cachedRealm->_realm->schema());
- realm->_schema = cachedRealm.schema;
- realm->_info = cachedRealm->_info.clone(cachedRealm->_realm->schema(), realm);
- realmIsCached = true;
- }
- }
- bool isFirstOpen = false;
- if (realm->_schema) { }
- else if (dynamic) {
- realm->_schema = [RLMSchema dynamicSchemaFromObjectStoreSchema:realm->_realm->schema()];
- realm->_info = RLMSchemaInfo(realm);
- }
- else {
- // set/align schema or perform migration if needed
- RLMSchema *schema = configuration.customSchema ?: RLMSchema.sharedSchema;
- MigrationFunction migrationFunction;
- auto migrationBlock = configuration.migrationBlock;
- if (migrationBlock && configuration.schemaVersion > 0) {
- migrationFunction = [=](SharedRealm old_realm, SharedRealm realm, Schema& mutableSchema) {
- RLMSchema *oldSchema = [RLMSchema dynamicSchemaFromObjectStoreSchema:old_realm->schema()];
- RLMRealm *oldRealm = [RLMRealm realmWithSharedRealm:old_realm
- schema:oldSchema
- dynamic:true];
- // The destination RLMRealm can't just use the schema from the
- // SharedRealm because it doesn't have information about whether or
- // not a class was defined in Swift, which effects how new objects
- // are created
- RLMRealm *newRealm = [RLMRealm realmWithSharedRealm:realm
- schema:schema.copy
- dynamic:true];
- [[[RLMMigration alloc] initWithRealm:newRealm oldRealm:oldRealm schema:mutableSchema]
- execute:migrationBlock objectClass:configuration.migrationObjectClass];
- oldRealm->_realm = nullptr;
- newRealm->_realm = nullptr;
- };
- }
- DataInitializationFunction initializationFunction;
- if (!configuration.rerunOnOpen && configuration.initialSubscriptions) {
- initializationFunction = [&isFirstOpen](SharedRealm) {
- isFirstOpen = true;
- };
- }
- try {
- realm->_realm->update_schema(schema.objectStoreCopy, config.schema_version,
- std::move(migrationFunction), std::move(initializationFunction));
- }
- catch (...) {
- RLMRealmTranslateException(error);
- return nil;
- }
- realm->_schema = schema;
- realm->_info = RLMSchemaInfo(realm);
- RLMSchemaEnsureAccessorsCreated(realm.schema);
- if (!configuration.readOnly) {
- REALM_ASSERT(!realm->_realm->is_in_read_transaction());
- if (s_set_skip_backup_attribute) {
- RLMAddSkipBackupAttributeToItemAtPath(config.path + ".management");
- RLMAddSkipBackupAttributeToItemAtPath(config.path + ".lock");
- RLMAddSkipBackupAttributeToItemAtPath(config.path + ".note");
- }
- }
- }
- if (cache) {
- RLMCacheRealm(configuration, scheduler, realm);
- }
- if (!configuration.readOnly) {
- realm->_realm->m_binding_context = RLMCreateBindingContext(realm);
- realm->_realm->m_binding_context->realm = realm->_realm;
- }
- #if REALM_ENABLE_SYNC
- if (isFirstOpen || (configuration.rerunOnOpen && !realmIsCached)) {
- RLMSyncSubscriptionSet *subscriptions = realm.subscriptions;
- [subscriptions update:^{
- configuration.initialSubscriptions(subscriptions);
- }];
- }
- #endif
- return realm;
- }
- + (void)resetRealmState {
- RLMClearRealmCache();
- realm::_impl::RealmCoordinator::clear_cache();
- [RLMRealmConfiguration resetRealmConfigurationState];
- }
- - (void)verifyNotificationsAreSupported:(bool)isCollection {
- [self verifyThread];
- if (_realm->config().immutable()) {
- @throw RLMException(@"Read-only Realms do not change and do not have change notifications.");
- }
- if (_realm->is_frozen()) {
- @throw RLMException(@"Frozen Realms do not change and do not have change notifications.");
- }
- if (_realm->config().automatic_change_notifications && !_realm->can_deliver_notifications()) {
- @throw RLMException(@"Can only add notification blocks from within runloops.");
- }
- if (isCollection && _realm->is_in_transaction()) {
- @throw RLMException(@"Cannot register notification blocks from within write transactions.");
- }
- }
- - (RLMNotificationToken *)addNotificationBlock:(RLMNotificationBlock)block {
- if (!block) {
- @throw RLMException(@"The notification block should not be nil");
- }
- [self verifyNotificationsAreSupported:false];
- _realm->read_group();
- if (!_notificationHandlers) {
- _notificationHandlers = [NSHashTable hashTableWithOptions:NSPointerFunctionsWeakMemory];
- }
- RLMRealmNotificationToken *token = [[RLMRealmNotificationToken alloc] init];
- token.realm = self;
- token.block = block;
- [_notificationHandlers addObject:token];
- return token;
- }
- - (void)sendNotifications:(RLMNotification)notification {
- NSAssert(!_realm->config().immutable(), @"Read-only realms do not have notifications");
- if (_sendingNotifications) {
- return;
- }
- NSUInteger count = _notificationHandlers.count;
- if (count == 0) {
- return;
- }
- _sendingNotifications = true;
- auto cleanup = realm::util::make_scope_exit([&]() noexcept {
- _sendingNotifications = false;
- });
- // call this realm's notification blocks
- if (count == 1) {
- if (auto block = [_notificationHandlers.anyObject block]) {
- block(notification, self);
- }
- }
- else {
- for (RLMRealmNotificationToken *token in _notificationHandlers.allObjects) {
- if (auto block = token.block) {
- block(notification, self);
- }
- }
- }
- }
- - (RLMRealmConfiguration *)configuration {
- RLMRealmConfiguration *configuration = [[RLMRealmConfiguration alloc] init];
- configuration.configRef = _realm->config();
- configuration.dynamic = _dynamic;
- configuration.customSchema = _schema;
- return configuration;
- }
- - (void)beginWriteTransaction {
- [self beginWriteTransactionWithError:nil];
- }
- - (BOOL)beginWriteTransactionWithError:(NSError **)error {
- try {
- _realm->begin_transaction();
- return YES;
- }
- catch (...) {
- RLMRealmTranslateException(error);
- return NO;
- }
- }
- - (void)commitWriteTransaction {
- [self commitWriteTransaction:nil];
- }
- - (BOOL)commitWriteTransaction:(NSError **)error {
- return [self commitWriteTransactionWithoutNotifying:@[] error:error];
- }
- - (BOOL)commitWriteTransactionWithoutNotifying:(NSArray<RLMNotificationToken *> *)tokens error:(NSError **)error {
- for (RLMNotificationToken *token in tokens) {
- if (token.realm != self) {
- @throw RLMException(@"Incorrect Realm: only notifications for the Realm being modified can be skipped.");
- }
- [token suppressNextNotification];
- }
- try {
- _realm->commit_transaction();
- return YES;
- }
- catch (...) {
- RLMRealmTranslateException(error);
- return NO;
- }
- }
- - (void)transactionWithBlock:(__attribute__((noescape)) void(^)(void))block {
- [self transactionWithBlock:block error:nil];
- }
- - (BOOL)transactionWithBlock:(__attribute__((noescape)) void(^)(void))block error:(NSError **)outError {
- return [self transactionWithoutNotifying:@[] block:block error:outError];
- }
- - (void)transactionWithoutNotifying:(NSArray<RLMNotificationToken *> *)tokens block:(__attribute__((noescape)) void(^)(void))block {
- [self transactionWithoutNotifying:tokens block:block error:nil];
- }
- - (BOOL)transactionWithoutNotifying:(NSArray<RLMNotificationToken *> *)tokens block:(__attribute__((noescape)) void(^)(void))block error:(NSError **)error {
- [self beginWriteTransactionWithError:error];
- block();
- if (_realm->is_in_transaction()) {
- return [self commitWriteTransactionWithoutNotifying:tokens error:error];
- }
- return YES;
- }
- - (void)cancelWriteTransaction {
- try {
- _realm->cancel_transaction();
- }
- catch (std::exception &ex) {
- @throw RLMException(ex);
- }
- }
- - (BOOL)isPerformingAsynchronousWriteOperations {
- return _realm->is_in_async_transaction();
- }
- - (RLMAsyncTransactionId)beginAsyncWriteTransaction:(void(^)())block {
- try {
- return _realm->async_begin_transaction(block);
- }
- catch (std::exception &ex) {
- @throw RLMException(ex);
- }
- }
- - (RLMAsyncTransactionId)commitAsyncWriteTransaction {
- try {
- return _realm->async_commit_transaction();
- }
- catch (...) {
- RLMRealmTranslateException(nil);
- return 0;
- }
- }
- - (RLMAsyncWriteTask *)beginAsyncWrite {
- try {
- auto write = [[RLMAsyncWriteTask alloc] initWithRealm:self];
- write.transactionId = _realm->async_begin_transaction(^{ [write complete:false]; }, true);
- return write;
- }
- catch (std::exception &ex) {
- @throw RLMException(ex);
- }
- }
- - (void)commitAsyncWriteWithGrouping:(bool)allowGrouping
- completion:(void(^)(NSError *_Nullable))completion {
- [self commitAsyncWriteTransaction:completion allowGrouping:allowGrouping];
- }
- - (RLMAsyncTransactionId)commitAsyncWriteTransaction:(void(^)(NSError *))completionBlock {
- return [self commitAsyncWriteTransaction:completionBlock allowGrouping:false];
- }
- - (RLMAsyncTransactionId)commitAsyncWriteTransaction:(nullable void(^)(NSError *))completionBlock
- allowGrouping:(BOOL)allowGrouping {
- try {
- auto completion = [=](std::exception_ptr err) {
- @autoreleasepool {
- if (!completionBlock) {
- std::rethrow_exception(err);
- return;
- }
- if (err) {
- try {
- std::rethrow_exception(err);
- }
- catch (...) {
- NSError *error;
- RLMRealmTranslateException(&error);
- completionBlock(error);
- }
- } else {
- completionBlock(nil);
- }
- }
- };
- if (completionBlock) {
- return _realm->async_commit_transaction(completion, allowGrouping);
- }
- return _realm->async_commit_transaction(nullptr, allowGrouping);
- }
- catch (...) {
- RLMRealmTranslateException(nil);
- return 0;
- }
- }
- - (void)cancelAsyncTransaction:(RLMAsyncTransactionId)asyncTransactionId {
- try {
- _realm->async_cancel_transaction(asyncTransactionId);
- }
- catch (std::exception &ex) {
- @throw RLMException(ex);
- }
- }
- - (RLMAsyncTransactionId)asyncTransactionWithBlock:(void(^)())block onComplete:(nullable void(^)(NSError *))completionBlock {
- return [self beginAsyncWriteTransaction:^{
- block();
- if (_realm->is_in_transaction()) {
- [self commitAsyncWriteTransaction:completionBlock];
- }
- }];
- }
- - (RLMAsyncTransactionId)asyncTransactionWithBlock:(void(^)())block {
- return [self beginAsyncWriteTransaction:^{
- block();
- if (_realm->is_in_transaction()) {
- [self commitAsyncWriteTransaction];
- }
- }];
- }
- - (void)invalidate {
- if (_realm->is_in_transaction()) {
- NSLog(@"WARNING: An RLMRealm instance was invalidated during a write "
- "transaction and all pending changes have been rolled back.");
- }
- [self detachAllEnumerators];
- for (auto& objectInfo : _info) {
- for (RLMObservationInfo *info : objectInfo.second.observedObjects) {
- info->willChange(RLMInvalidatedKey);
- }
- }
- _realm->invalidate();
- for (auto& objectInfo : _info) {
- for (RLMObservationInfo *info : objectInfo.second.observedObjects) {
- info->didChange(RLMInvalidatedKey);
- }
- }
- if (_realm->is_frozen()) {
- _realm->close();
- }
- }
- - (nullable id)resolveThreadSafeReference:(RLMThreadSafeReference *)reference {
- return [reference resolveReferenceInRealm:self];
- }
- /**
- Replaces all string columns in this Realm with a string enumeration column and compacts the
- database file.
- Cannot be called from a write transaction.
- Compaction will not occur if other `RLMRealm` instances exist.
- While compaction is in progress, attempts by other threads or processes to open the database will
- wait.
- Be warned that resource requirements for compaction is proportional to the amount of live data in
- the database.
- Compaction works by writing the database contents to a temporary database file and then replacing
- the database with the temporary one. The name of the temporary file is formed by appending
- `.tmp_compaction_space` to the name of the database.
- @return YES if the compaction succeeded.
- */
- - (BOOL)compact {
- // compact() automatically ends the read transaction, but we need to clean
- // up cached state and send invalidated notifications when that happens, so
- // explicitly end it first unless we're in a write transaction (in which
- // case compact() will throw an exception)
- if (!_realm->is_in_transaction()) {
- [self invalidate];
- }
- try {
- return _realm->compact();
- }
- catch (std::exception const& ex) {
- @throw RLMException(ex);
- }
- }
- - (void)dealloc {
- if (_realm) {
- if (_realm->is_in_transaction()) {
- [self cancelWriteTransaction];
- NSLog(@"WARNING: An RLMRealm instance was deallocated during a write transaction and all "
- "pending changes have been rolled back. Make sure to retain a reference to the "
- "RLMRealm for the duration of the write transaction.");
- }
- }
- }
- - (BOOL)refresh {
- if (_realm->config().immutable()) {
- @throw RLMException(@"Read-only Realms do not change and cannot be refreshed.");
- }
- try {
- return _realm->refresh();
- }
- catch (std::exception const& e) {
- @throw RLMException(e);
- }
- }
- - (void)addObject:(__unsafe_unretained RLMObject *const)object {
- RLMAddObjectToRealm(object, self, RLMUpdatePolicyError);
- }
- - (void)addObjects:(id<NSFastEnumeration>)objects {
- for (RLMObject *obj in objects) {
- if (![obj isKindOfClass:RLMObjectBase.class]) {
- @throw RLMException(@"Cannot insert objects of type %@ with addObjects:. Only RLMObjects are supported.",
- NSStringFromClass(obj.class));
- }
- [self addObject:obj];
- }
- }
- - (void)addOrUpdateObject:(RLMObject *)object {
- // verify primary key
- if (!object.objectSchema.primaryKeyProperty) {
- @throw RLMException(@"'%@' does not have a primary key and can not be updated", object.objectSchema.className);
- }
- RLMAddObjectToRealm(object, self, RLMUpdatePolicyUpdateAll);
- }
- - (void)addOrUpdateObjects:(id<NSFastEnumeration>)objects {
- for (RLMObject *obj in objects) {
- if (![obj isKindOfClass:RLMObjectBase.class]) {
- @throw RLMException(@"Cannot add or update objects of type %@ with addOrUpdateObjects:. Only RLMObjects are"
- " supported.",
- NSStringFromClass(obj.class));
- }
- [self addOrUpdateObject:obj];
- }
- }
- - (void)deleteObject:(RLMObject *)object {
- RLMDeleteObjectFromRealm(object, self);
- }
- - (void)deleteObjects:(id<NSFastEnumeration>)objects {
- id idObjects = objects;
- if ([idObjects respondsToSelector:@selector(realm)]
- && [idObjects respondsToSelector:@selector(deleteObjectsFromRealm)]) {
- if (self != (RLMRealm *)[idObjects realm]) {
- @throw RLMException(@"Can only delete objects from the Realm they belong to.");
- }
- [idObjects deleteObjectsFromRealm];
- return;
- }
- if (auto array = RLMDynamicCast<RLMArray>(objects)) {
- if (array.type != RLMPropertyTypeObject) {
- @throw RLMException(@"Cannot delete objects from RLMArray<%@>: only RLMObjects can be deleted.",
- RLMTypeToString(array.type));
- }
- }
- else if (auto set = RLMDynamicCast<RLMSet>(objects)) {
- if (set.type != RLMPropertyTypeObject) {
- @throw RLMException(@"Cannot delete objects from RLMSet<%@>: only RLMObjects can be deleted.",
- RLMTypeToString(set.type));
- }
- }
- else if (auto dictionary = RLMDynamicCast<RLMDictionary>(objects)) {
- if (dictionary.type != RLMPropertyTypeObject) {
- @throw RLMException(@"Cannot delete objects from RLMDictionary of type %@: only RLMObjects can be deleted.",
- RLMTypeToString(dictionary.type));
- }
- for (RLMObject *obj in dictionary.allValues) {
- RLMDeleteObjectFromRealm(obj, self);
- }
- return;
- }
- for (RLMObject *obj in objects) {
- if (![obj isKindOfClass:RLMObjectBase.class]) {
- @throw RLMException(@"Cannot delete objects of type %@ with deleteObjects:. Only RLMObjects can be deleted.",
- NSStringFromClass(obj.class));
- }
- RLMDeleteObjectFromRealm(obj, self);
- }
- }
- - (void)deleteAllObjects {
- RLMDeleteAllObjectsFromRealm(self);
- }
- - (RLMResults *)allObjects:(NSString *)objectClassName {
- return RLMGetObjects(self, objectClassName, nil);
- }
- - (RLMResults *)objects:(NSString *)objectClassName where:(NSString *)predicateFormat, ... {
- va_list args;
- va_start(args, predicateFormat);
- RLMResults *results = [self objects:objectClassName where:predicateFormat args:args];
- va_end(args);
- return results;
- }
- - (RLMResults *)objects:(NSString *)objectClassName where:(NSString *)predicateFormat args:(va_list)args {
- return [self objects:objectClassName withPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]];
- }
- - (RLMResults *)objects:(NSString *)objectClassName withPredicate:(NSPredicate *)predicate {
- return RLMGetObjects(self, objectClassName, predicate);
- }
- - (RLMObject *)objectWithClassName:(NSString *)className forPrimaryKey:(id)primaryKey {
- return RLMGetObject(self, className, primaryKey);
- }
- + (uint64_t)schemaVersionAtURL:(NSURL *)fileURL encryptionKey:(NSData *)key error:(NSError **)error {
- RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init];
- config.fileURL = fileURL;
- config.encryptionKey = RLMRealmValidatedEncryptionKey(key);
- uint64_t version = RLMNotVersioned;
- try {
- version = Realm::get_schema_version(config.configRef);
- }
- catch (...) {
- RLMRealmTranslateException(error);
- return version;
- }
- if (error && version == realm::ObjectStore::NotVersioned) {
- auto msg = [NSString stringWithFormat:@"Realm at path '%@' has not been initialized.", fileURL.path];
- *error = [NSError errorWithDomain:RLMErrorDomain
- code:RLMErrorInvalidDatabase
- userInfo:@{NSLocalizedDescriptionKey: msg,
- NSFilePathErrorKey: fileURL.path}];
- }
- return version;
- }
- + (BOOL)performMigrationForConfiguration:(RLMRealmConfiguration *)configuration error:(NSError **)error {
- if (RLMGetAnyCachedRealmForPath(configuration.path)) {
- @throw RLMException(@"Cannot migrate Realms that are already open.");
- }
- NSError *localError; // Prevents autorelease
- BOOL success;
- @autoreleasepool {
- success = [RLMRealm realmWithConfiguration:configuration error:&localError] != nil;
- }
- if (!success && error) {
- *error = localError; // Must set outside pool otherwise will free anyway
- }
- return success;
- }
- - (RLMObject *)createObject:(NSString *)className withValue:(id)value {
- return (RLMObject *)RLMCreateObjectInRealmWithValue(self, className, value, RLMUpdatePolicyError);
- }
- - (BOOL)writeCopyToURL:(NSURL *)fileURL encryptionKey:(NSData *)key error:(NSError **)error {
- RLMRealmConfiguration *configuration = [RLMRealmConfiguration new];
- configuration.fileURL = fileURL;
- configuration.encryptionKey = key;
- return [self writeCopyForConfiguration:configuration error:error];
- }
- - (BOOL)writeCopyForConfiguration:(RLMRealmConfiguration *)configuration error:(NSError **)error {
- try {
- _realm->convert(configuration.configRef, false);
- return YES;
- }
- catch (...) {
- if (error) {
- RLMRealmTranslateException(error);
- }
- }
- return NO;
- }
- + (BOOL)fileExistsForConfiguration:(RLMRealmConfiguration *)config {
- return [NSFileManager.defaultManager fileExistsAtPath:config.pathOnDisk];
- }
- + (BOOL)deleteFilesForConfiguration:(RLMRealmConfiguration *)config error:(NSError **)error {
- bool didDeleteAny = false;
- try {
- realm::Realm::delete_files(config.path, &didDeleteAny);
- }
- catch (realm::FileAccessError const& e) {
- if (error) {
- // For backwards compatibility, but this should go away in 11.0
- if (e.code() == realm::ErrorCodes::PermissionDenied) {
- *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteNoPermissionError
- userInfo:@{NSLocalizedDescriptionKey: @(e.what()),
- NSFilePathErrorKey: @(e.get_path().data())}];
- }
- else {
- RLMRealmTranslateException(error);
- }
- }
- }
- catch (...) {
- if (error) {
- RLMRealmTranslateException(error);
- }
- }
- return didDeleteAny;
- }
- - (BOOL)isFrozen {
- return _realm->is_frozen();
- }
- - (RLMRealm *)freeze {
- [self verifyThread];
- return self.isFrozen ? self : RLMGetFrozenRealmForSourceRealm(self);
- }
- - (RLMRealm *)thaw {
- [self verifyThread];
- return self.isFrozen ? [RLMRealm realmWithConfiguration:self.configuration error:nil] : self;
- }
- - (RLMRealm *)frozenCopy {
- try {
- RLMRealm *realm = [[RLMRealm alloc] initPrivate];
- realm->_realm = _realm->freeze();
- realm->_realm->read_group();
- realm->_dynamic = _dynamic;
- realm->_schema = _schema;
- realm->_info = RLMSchemaInfo(realm);
- return realm;
- }
- catch (std::exception const& e) {
- @throw RLMException(e);
- }
- }
- - (void)registerEnumerator:(RLMFastEnumerator *)enumerator {
- std::lock_guard lock(_collectionEnumeratorMutex);
- if (!_collectionEnumerators) {
- _collectionEnumerators = [NSHashTable hashTableWithOptions:NSPointerFunctionsWeakMemory];
- }
- [_collectionEnumerators addObject:enumerator];
- }
- - (void)unregisterEnumerator:(RLMFastEnumerator *)enumerator {
- std::lock_guard lock(_collectionEnumeratorMutex);
- [_collectionEnumerators removeObject:enumerator];
- }
- - (void)detachAllEnumerators {
- std::lock_guard lock(_collectionEnumeratorMutex);
- for (RLMFastEnumerator *enumerator in _collectionEnumerators) {
- [enumerator detach];
- }
- _collectionEnumerators = nil;
- }
- - (bool)isFlexibleSync {
- #if REALM_ENABLE_SYNC
- return _realm->config().sync_config && _realm->config().sync_config->flx_sync_requested;
- #else
- return false;
- #endif
- }
- - (RLMSyncSubscriptionSet *)subscriptions {
- #if REALM_ENABLE_SYNC
- if (!self.isFlexibleSync) {
- @throw RLMException(@"This Realm was not configured with flexible sync");
- }
- return [[RLMSyncSubscriptionSet alloc] initWithSubscriptionSet:_realm->get_latest_subscription_set() realm:self];
- #else
- @throw RLMException(@"Realm was not compiled with sync enabled");
- #endif
- }
- @end
|