简体   繁体   中英

How do you compact a Realm DB on iOS?

I'd like to compact a Realm instance on iOS periodically to recover space. I think the process is to copy the db to a temporary location, then copy it back and use the new default.realm file.

My problem is Realm() acts like a singleton and recycles objects so I can't really close it and tell it to open the new default.realm file.

The docs here ( https://realm.io/docs/objc/latest/api/Classes/RLMRealm.html ) suggest I wrap all the Realm() calls in autorelease { } but it can't be this complicated.

It can be indeed tricky to completely tear down all retrieved model accessors, but there is unfortunately no other way to close a Realm.

As you wrote "periodically" every app launch might be often enough, depending on your use case.

On the launch of your application, it should be still relatively easy to open Realm in a dedicated autoreleasepool, write a compacted copy to a different path and replace your default.realm file with it.

Swift 2.1

func compactRealm() {
    let defaultURL = Realm.Configuration.defaultConfiguration.fileURL!
    let defaultParentURL = defaultURL.URLByDeletingLastPathComponent!
    let compactedURL = defaultParentURL.URLByAppendingPathComponent("default-compact.realm")

    autoreleasepool {
        let realm = try! Realm()
        realm.writeCopyToPath(compactedURL)
    }
    try! NSFileManager.defaultManager().removeItemAtURL(defaultURL)
    try! NSFileManager.defaultManager().moveItemAtURL(compactedURL, toURL: defaultURL)
}

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    compactRealm()

    // further setup …

    return true
}

Swift 3.0

func compactRealm() {
    let defaultURL = Realm.Configuration.defaultConfiguration.fileURL!
    let defaultParentURL = defaultURL.deletingLastPathComponent()
    let compactedURL = defaultParentURL.appendingPathComponent("default-compact.realm")

    autoreleasepool {
        let realm = try! Realm()
        try! realm.writeCopy(toFile: compactedURL)
    }
    try! FileManager.default.removeItem(at: defaultURL)
    try! FileManager.default.moveItem(at: compactedURL, to: defaultURL)
}

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    compactRealm()

    // further setup …

    return true
}

The answer given by @marius has an issue: the open Realm might still reference the deleted file. This means some writes might end up in the old (deleted) file, causing the app to lose data.

The correct implementation of compactRealm method looks like this (swift 3):

func compactRealm() {
    let defaultURL = Realm.Configuration.defaultConfiguration.fileURL!
    let defaultParentURL = defaultURL.deletingLastPathComponent()
    let compactedURL = defaultParentURL.appendingPathComponent("default-compact.realm")

    autoreleasepool {
        let realm = try! Realm()
        try! realm.writeCopy(toFile: compactedURL)
    }
    try! FileManager.default.removeItem(at: defaultURL)
    try! FileManager.default.moveItem(at: compactedURL, to: defaultURL)
}

This issue has been driving me crazy until I found an answer here

Well.. it appears as though this issue is mostly obsolete. Realm added an automatic compact feature last fall. Realm Docs / compacting-realms . I think the only reason to do it as described by @marius is if you need to control the user experience and compact in the background.

See this question for more: How to correctly use shouldCompactOnLaunch in RealmSwift

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM