Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get the size of data present in coredata store?

How to get the total memory size in bytes of data present in core data store.And How to get the memory size for particular rows of data.

like image 241
Deepak Avatar asked Jun 12 '14 07:06

Deepak


2 Answers

On iOS 7 and above, older answers don't work anymore for SQLite-backed persistent stores, because of SQLite journal files. For each persistent store file you need to get the size of the SQLite file itself (e.g. Foo.sqlite) and the sizes of the journal files (e.g. Foo.sqlite-wal and Foo.sqlite-shm) and add the sizes to get the total. This is really important because it's very likely that most of the data is actually in the journal files. You can use NSFileManager to get this information for each file.

If you're using binary attributes in your model and you have "Allows External Storage" enabled for any of those attributes, it gets more complicated. In that case you need to find all of the external storage files and add up their sizes too. Their location is not documented but should be in a subdirectory of the directory where the SQLite file is found. For a SQLite file named Foo.sqlite look for a directory named .Foo_SUPPORT/_EXTERNAL_DATA, and the external binary files will be there. Since this is not documented, it may be subject to change without warning.

A better approach-- if it's not too late-- is to put the persistent store in its own subdirectory. Instead of putting it in the documents directory, create a new directory inside documents and put your SQLite file there. That is, create a directory called something like data and put Foo.sqlite in it. To add up the size, just recursively scan every file in data and add up the sizes. Then you'll catch everything, even if journaling or external binaries or whatever change in future versions of iOS. A good way to do the scan would be with NSFileManager's enumeratorAtPath: or enumeratorAtURL:includingPropertiesForKeys:options:errorHandler: methods.

Finding the memory size for a particular row of data is a very different question, and there's no general solution. There's the overhead of any object instance, plus your attributes, plus any internal undocumented objects NSManagedObject might create (including instance vars and dynamically allocated memory). It's not even a fixed value since data can be allocated and released on the fly. Adding up the sizes of just your attributes is easy-- just run through them and add up the size of each (string length, data length, etc), but it's not the full picture.

like image 72
Tom Harrington Avatar answered Oct 31 '22 06:10

Tom Harrington


Using Swift 5, I came up with this answer. It not only gets the size of the store and files, but also formats the data size using Apples ByteCountFormatter.

public func getSqliteStoreSize(forPersistentContainerUrl storeUrl: URL) -> String {
    do {
        let size = try Data(contentsOf: storeUrl)
        if size.count < 1 {
            print("Size could not be determined.")
            return ""
        }
        let bcf = ByteCountFormatter()
        bcf.allowedUnits = [.useMB] // optional: restricts the units to MB only
        bcf.countStyle = .file
        let string = bcf.string(fromByteCount: Int64(size.count))
        print(string)
        return string
    } catch {
        print("Failed to get size of store: \(error)")
        return ""
    }
}

To use is as simple as:

// Get Core Data storage size:
guard let storeUrl = self.managedObjectContext!.persistentStoreCoordinator!.persistentStores.first?.url else {
    print("There is no store url")
    return
}
print("The size of the store is: \(self.getSqliteStoreSize(forPersistentContainerUrl: storeUrl))")

One minor detail here is to ensure you check that your moc and persistentStoreCoordinator actually have value - in my case I know that they do because of how I have initialised them.

Regarding the answer above that takes into account the wal and shm files, you can check these by appending a string to the URL:

let newFileName = storeUrl.absoluteString.appending("\("-wal")")
let newFileName2 = storeUrl.absoluteString.appending("\("-shm")")
print("The size of the wal is: \(self.getSqliteStoreSize(forPersistentContainerUrl: URL(string: newFileName)!))")
print("The size of the shm is: \(self.getSqliteStoreSize(forPersistentContainerUrl: URL(string: newFileName2)!))")
like image 21
App Dev Guy Avatar answered Oct 31 '22 05:10

App Dev Guy