在MagicalRecord中清空(移除)数据库。

15

我有一个使用MagicalRecord处理核心数据的应用程序,这很不错。然而,我有不同的用户可以登录应用程序,当另一个用户登录时,核心数据数据库必须被清空,以便不同的用户可以拥有自己的数据。数据库可以完全清空,因为数据也存储在Web服务上,因此在再次登录第一个用户后始终可以进行同步。

到目前为止,我似乎找不到一个适合此目的的帮助程序(能够工作)。我已经尝试过:

[MagicalRecord cleanUp];

每当用户注销,但这并不能解决问题。


2
我不知道Magical Record是否有管理这个的功能。你需要等待作者来解决这个问题。无论如何,你可以通过删除创建的db文件并通过MagicalRecord重新初始化堆栈来简单地绕过它。也许你可以在MagicalRecord项目中打开一个拉取请求(需要GitHub账户)。 - Lorenzo B
6个回答

38

这是我是如何做到的。有一行代码是必需的:[MagicalRecord cleanup]。没有它,[self setupDB] 就无法工作。

更新:删除-wal和-shm文件。@thattyson指出iOS 9存在问题。此外,查看@onmyway133的答案

- (void)setupDB
{
    [MagicalRecord setupCoreDataStackWithAutoMigratingSqliteStoreNamed:[self dbStore]];
}

- (NSString *)dbStore
{
    NSString *bundleID = (NSString *)[[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleIdentifierKey];
    return [NSString stringWithFormat:@"%@.sqlite", bundleID];
}

- (void)cleanAndResetupDB
{
    NSString *dbStore = [self dbStore];

    NSError *error1 = nil;
    NSError *error2 = nil;
    NSError *error3 = nil;

    NSURL *storeURL = [NSPersistentStore MR_urlForStoreName:dbStore];
    NSURL *walURL = [[storeURL URLByDeletingPathExtension] URLByAppendingPathExtension:@"sqlite-wal"];
    NSURL *shmURL = [[storeURL URLByDeletingPathExtension] URLByAppendingPathExtension:@"sqlite-shm"];

    [MagicalRecord cleanUp];

    if([[NSFileManager defaultManager] removeItemAtURL:storeURL error:&error1] && [[NSFileManager defaultManager] removeItemAtURL:walURL error:&error2] && [[NSFileManager defaultManager] removeItemAtURL:shmURL error:&error3]){
        [self setupDB];
    }
    else{
        NSLog(@"An error has occurred while deleting %@", dbStore);
        NSLog(@"Error1 description: %@", error1.description);
        NSLog(@"Error2 description: %@", error2.description);
        NSLog(@"Error3 description: %@", error3.description);
    }
}

这是 Swift 版本:

func setupDB() {
    MagicalRecord.setupCoreDataStackWithAutoMigratingSqliteStoreNamed(self.dbStore())
}

func dbStore() -> String {
    return "\(self.bundleID()).sqlite"
}

func bundleID() -> String {
    return NSBundle.mainBundle().bundleIdentifier!
}

func cleanAndResetupDB() {
    let dbStore = self.dbStore()

    let url = NSPersistentStore.MR_urlForStoreName(dbStore)
    let walURL = url.URLByDeletingPathExtension?.URLByAppendingPathExtension("sqlite-wal")
    let shmURL = url.URLByDeletingPathExtension?.URLByAppendingPathExtension("sqlite-shm")

    var removeError: NSError?

    MagicalRecord.cleanUp()

    //Swift 1
    //let deleteSuccess = NSFileManager.defaultManager().removeItemAtURL(url, error: &removeError)

    //Swift 2
    let deleteSuccess: Bool
    do {
        try NSFileManager.defaultManager().removeItemAtURL(url)
        try NSFileManager.defaultManager().removeItemAtURL(walURL!)
        try NSFileManager.defaultManager().removeItemAtURL(shmURL!)
        deleteSuccess = true
    } catch let error as NSError {
        removeError = error
        deleteSuccess = false
    }

    if deleteSuccess {
        self.setupDB()
    } else {
        println("An error has occured while deleting \(dbStore)")
        println("Error description: \(removeError?.description)")
    }
}

3
那么关于 -shm 和 -wal 文件呢?这种方式不会删除它们,对吗? - swalkner
1
对我不起作用。我收到以下错误:“无法完成操作。没有这样的文件或目录”。 - Szu
从iOS9开始,如果您不删除-shm和-wal文件,您的应用程序在尝试第二次设置数据库后将崩溃,并显示错误SQLite error code:522 - thattyson
@thattyson,你的意思是如果你的基础SDK是iOS 9吗? - yoninja
1
@yoninja 抱歉,如果您的基本SDK是iOS 9,则删除和设置将起作用,但在尝试访问新DB时会遇到错误和崩溃。在self.setupDB()之前删除-wal和-shm可以解决此问题。 - thattyson
显示剩余3条评论

20
为了进一步解释 @yoninja 的答案,这将显式地重置CoreData堆栈,同时处理wal和shm文件。

扩展 @yoninja 的答案,这将明确重置CoreData堆栈,还要处理wal和shm文件。

- (void)setupDB
{
    [MagicalRecord setDefaultModelNamed:@"Model.momd"];
    [MagicalRecord setupCoreDataStack];
}

- (void)cleanAndResetupDB
{
    [MagicalRecord cleanUp];

    NSString *dbStore = [MagicalRecord defaultStoreName];

    NSURL *storeURL = [NSPersistentStore MR_urlForStoreName:dbStore];
    NSURL *walURL = [[storeURL URLByDeletingPathExtension] URLByAppendingPathExtension:@"sqlite-wal"];
    NSURL *shmURL = [[storeURL URLByDeletingPathExtension] URLByAppendingPathExtension:@"sqlite-shm"];

    NSError *error = nil;
    BOOL result = YES;

    for (NSURL *url in @[storeURL, walURL, shmURL]) {
        if ([[NSFileManager defaultManager] fileExistsAtPath:url.path]) {
            result = [[NSFileManager defaultManager] removeItemAtURL:url error:&error];
        }
    }

    if (result) {
        [self setupDB];
    } else {
        NSLog(@"An error has occurred while deleting %@ error %@", dbStore, error);
    }
}

这对我也起作用,但似乎有时候在注销后,后台线程上的私有上下文会被悬挂。有什么办法可以解决吗?感谢您的帮助。 - Myxtic
为什么这些不是类函数? - Minimi

18

MagicalRecord不为你提供此功能。 cleanUp方法可为你重新初始化内存中的CoreData堆栈并清理任何上下文、队列和其他相关对象。然而,鉴于MagicalRecord提供了一个方便的方法来获取库的路径,因此自己实现也不是那么困难。

请查看-[NSPersistentStore MR_urlForStoreName:]方法。这将为你提供存储的文件URL。然后,你可以使用NSFileManager实例删除它。要小心,在设置好Core Data堆栈之前执行此操作,否则当你尝试保存时会导致崩溃,因为已经从已正确初始化的堆栈中撤出了存储。


如果你看到我的评论,我建议了同样的事情;-) 对于你更深入的解释,我点赞。 - Lorenzo B
欢迎您在仓库上提出拉取请求或问题,我们非常乐意听取您的建议 :) - casademora
我刚在GitHub上提交了一个新问题。干杯。 - Lorenzo B
这个问题是否已经添加到 MR 中了? - lostintranslation

5
以下内容将完全删除MagicalRecord CoreData sqlite文件以及-wal和-shm文件。 MagicalRecord将它们全部放在Library文件夹中;这将仅从文件夹中删除所有文件。如果您有其他需要持续存在于Library文件夹中的数据,则此方法无法正常工作,但我没有:
- (void)resetCoreDataDB
{
    [MagicalRecord cleanUp];
    [self deleteFilesInLibrary];
    [MagicalRecord setupCoreDataStackWithAutoMigratingSqliteStoreNamed:@"YourDBName.sqlite"];
}

- (void)deleteFilesInLibraryDirectory
{
    NSString* folderPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    NSError *error = nil;
    for (NSString *file in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:folderPath error:&error])
    {
        [[NSFileManager defaultManager] removeItemAtPath:[folderPath stringByAppendingPathComponent:file] error:&error];
        if(error)
        {
            NSLog(@"Delete error: %@", error.description);
        }
    }
}

2

如果您正在使用iOS模拟器并删除了数据库文件,您可能会注意到数据仍然存在。但是,如果在实际设备上进行测试(应该这样做),则文件将被删除,并且上下文将被重置为应该的状态。

[MagicalRecord cleanUp];

// delete database file
NSError *error;
NSURL *fileURL = [NSPersistentStore MR_urlForStoreName:@"db.sqlite"];
[[NSFileManager defaultManager] removeItemAtURL:fileURL error:&error];
if(error) {
    // Hanldle error
}

// reset setup.
[MagicalRecord setupCoreDataStackWithAutoMigratingSqliteStoreNamed:@"db.sqlite"];

0

对于Swift 4,@yoninja的答案稍作修改:

private var dbStore : String? {
    get {
        if let bundleId = Bundle.main.bundleIdentifier {
            return bundleId + ".sqlite"
        }
        return MagicalRecord.defaultStoreName()
    }
}

func setupCoreDataStack() {
    MagicalRecord.setupCoreDataStack(withAutoMigratingSqliteStoreNamed: self.dbStore)
}

func cleanUp() {        
    MagicalRecord.cleanUp()

    var removeError: NSError?
    let deleteSuccess: Bool
    do {
        guard let url = NSPersistentStore.mr_url(forStoreName: self.dbStore) else {
            return
        }
        let walUrl = url.deletingPathExtension().appendingPathExtension("sqlite-wal")
        let shmUrl = url.deletingPathExtension().appendingPathExtension("sqlite-shm")

        try FileManager.default.removeItem(at: url)
        try FileManager.default.removeItem(at: walUrl)
        try FileManager.default.removeItem(at: shmUrl)

        deleteSuccess = true
    } catch let error as NSError {
        removeError = error
        deleteSuccess = false
    }

    if deleteSuccess {
        self.setupCoreDataStack()
    } else {
        print("An error has occured while deleting \(self.dbStore)")
        print("Error description: \(removeError.debugDescription)")
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接