密钥更改后的RLMException - 已经使用不同的加密密钥打开

时间:2017-08-30 19:29:20

标签: ios objective-c realm

我有一个应用程序,在注销后,出于合规性原因,需要擦除所有Realm数据并更改加密密钥。

RLMException "Realm at path ' ' already opened with different encryption key" after writeCopy(toFile:,encryptionKey:)

的情况并不完全相同

我确保在释放所有Realm对象后删除所有文件。

+ (void)deleteRealm:(BOOL)emptyDatabase numRetries:(NSUInteger)numRetries
{
    [[NSNotificationCenter defaultCenter] postNotificationName:@"AppDataStoreWillFlushDatabaseNotification" object:self];

    if (emptyDatabase)
    {
        @autoreleasepool {
            RLMRealm *realm = [RLMRealm defaultRealm];
            [realm transactionWithBlock:^{
                [realm deleteAllObjects];
            }];
            [realm invalidate];
        }
    }

    NSFileManager *manager = [NSFileManager defaultManager];
    RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
    NSArray<NSURL *> *realmFileURLs = @[
        config.fileURL,
        [config.fileURL URLByAppendingPathExtension:@"lock"],
        [config.fileURL URLByAppendingPathExtension:@"log_a"],
        [config.fileURL URLByAppendingPathExtension:@"log_b"],
        [config.fileURL URLByAppendingPathExtension:@"note"],
        [[config.fileURL URLByDeletingLastPathComponent] URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.realm.management", [[config.fileURL URLByDeletingPathExtension] lastPathComponent]]]
    ];
    for (NSURL *URL in realmFileURLs)
    {
        NSError *error = nil;
        [manager removeItemAtURL:URL error:&error];
        if (error)
        {
            // handle error
            DDLogError(@"Error deleting realm file - %@", error);
        }
    }

    // Remove old password
    [CHPasswordManager wipeKeyFromSecurityEnclave];

    [self configureRealm:(numRetries + 1)];
}

用以下方式重新创建:

+ (void)configureRealm:(NSUInteger)numRetries
{
    // Setup the encryption key
    NSString *encryptionKey = [CHPasswordManager encryptedStorePassphrase];

    // If encryption key is not valid anymore, generate a new one (CoreData used a 32 chars string while Realm uses a 64 chars string)
    if (encryptionKey.length == 32)
    {
        [CHPasswordManager wipeKeyFromSecurityEnclave];
        encryptionKey = [CHPasswordManager encryptedStorePassphrase];
    }

    NSData *key = [encryptionKey dataUsingEncoding:NSUTF8StringEncoding];
    RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];

    config.encryptionKey = key;

    // Set this as the configuration used for the default Realm
    [RLMRealmConfiguration setDefaultConfiguration:config];

    @autoreleasepool {
        @try {
            [RealmUser allObjects];
        }@catch (NSException *e) {
            if ([e.name isEqual:@"RLMException"])
            {
                // Something went wrong with encryption key - delete database and recreate again
                if (numRetries >= kRetriesThreshold)
                {
                    [Bugsnag notify:e];
                } else {
                    [self deleteRealm:NO numRetries:numRetries];
                }
            }
        }
    }
}

但是,我经常会遇到RLMException:

  

“理由:'道路上的境界   '/Users/myname/Library/Developer/CoreSimulator/Devices/152AC3D5-FD24-40DD-AFD7-5A3C4F6EE282/data/Containers/Data/Application/2F0140CF-68E4-4D0E-8AC0-BB869BEE9BF8/Documents/default.realm'   已经使用不同的加密密钥'“

打开

有趣的是:

  

“/用户/ MYNAME /库/开发商/ CoreSimulator /设备/ 152AC3D5-FD24-40DD-AFD7-5A3C4F6EE282 /数据/容器/数据/应用/ 2F0140CF-68E4-4D0E-8AC0-BB869BEE9BF8 /文档/ default.realm “

甚至不在文件系统上。

解决问题的任何提示?

1 个答案:

答案 0 :(得分:2)

就像他的另一个问题中概述的@bdash一样,当创建<ion-item [color]="colorCondition(item) ? 'primary' : 'light'"> //in your ts file colorCondition(item : ItemSliding){ let initItem = this.incompleteOrders.indexOf(item); let orderStatus = this.incompleteOrders[initItem].status; if(orderStatus == 'paid'){ return false; }else if(orderStatus == 'pending'){ return true; } } 实例且未明确包含在RLMRealm中时,Realm在内部缓存对该Realm的引用。这是设计使然,以确保Realm尽可能高效地运行。

虽然显而易见的解决方案是将应用程序中的每个Realm操作简单地括在@autoreleasepool中,但这将对性能造成不利影响,因为您将随后迫使Realm打开每个磁盘的新引用,并每次调用它。

我有一个可能的解决方案,在这里没有人直接提及:不要回收@autoreleasepool

这里的问题是,您正在创建default.realm的新副本,而在内存中仍然有对相同文件名的旧域的持久引用。

一个合理的折衷方法是,您每次执行注销操作时,都使用一个唯一的文件名创建一个新的Realm文件,而不是重新创建default.realm,然后将其设置为默认Realm。例如,您生成一个UUID字符串(例如default.realm)而不是default.realm,将对其文件名的引用存储在其他位置(7742e4bc-9f2b-44ae-acf1-7fb424438816.realm可能没问题),然后将其用作NSUserDefaults中的文件名。

这样,每当您需要删除旧的Realm并创建一个新的Realm时,新的Realm就会在您的应用中被视为一个全新的实体,并且缓存的剩余部分不会受到干扰,并且到那时就会消失该应用程序下次启动。 :)