Core Data provides several types of persistent store. This article describes the features and benefits of each, and how you can migrate from one type of store to another.
Important: In Mac OS X v10.4, there is no explicit class for persistent stores—you can only type a store instance as an id—consequently there is also no API for persistent store objects in Mac OS X v10.4. The techniques described below generally also apply to Mac OS X v10.4, but where a type is given as NSPersistentStore * you should use id.
Core Data provides three sorts of disk-based persistent store—XML, atomic, and SQLite—and an in-memory store. (Core Data provides the binary store type—NSBinaryStoreType—as a built-in atomic store; you can also create your own atomic store types—see “Custom store types.”) From the application code perspective, in general you should not be concerned about implementation details for any particular store. You should interact with managed objects and the persistence stack. There are, however, some behavioral differences between the types of store that you should consider when deciding what type of store to use.
XML | Atomic | SQLite | In-Memory | |
|---|---|---|---|---|
Speed | Slow | Fast | Fast | Fast |
Object Graph | Whole | Whole | Partial | Whole |
Other Factors | Externally parseable | No backing required |
Important: Although Core Data supports SQLite as a store type, the store format is private. You cannot create a SQLite database using native SQLite API and use it directly with Core Data (nor should you manipulate an existing Core Data SQLite store using native SQLite API). If you have an existing SQLite database, you need to import it into a Core Data store (see “Efficiently Importing Data”).
Given the abstraction that Core Data offers, there is typically no need to use the same store throughout the development process. It is common, for example, to use the XML store early in a project life-cycle, since it is fairly human-readable and you can inspect a file to determine whether or not it contains the data you expect. In a deployed application that uses a large data set, you typically use an SQLite store, since this offers high performance and does not require that the entire object graph reside in memory. You might use the binary store if you want store writes to be atomic. There are, however, some features and considerations that are specific to particular store types. These are described in following sections.
In Mac OS X v10.5 and later you can create your own atomic store types. For details, see Atomic Store Programming Topics.
In Mac OS X v10.4 , you cannot write your own object store which interoperates transparently with the Core Data stack. You can, however, manage object persistence yourself by using an in-memory store. Before you load your data, you create an in-memory store. When you load your data, you create instances of the appropriate model classes and insert them into a managed object context, associate them with the in-memory store (see insertObject: and assignObject:toPersistentStore:). The managed objects are then fully integrated into the Core Data stack and benefit from features such as undo management. You are also responsible, however, for saving the data. You must register to receive NSManagedObjectContextDidSaveNotification notifications from the managed object context, and upon receipt of the notification save the managed objects to the persistent store.
Core Data makes no guarantees regarding the security of persistent stores from untrusted sources and cannot detect whether files have been maliciously modified. The SQLite store offers slightly better security than the XML and binary stores, but it should not be considered inherently secure. Note that you should also consider the security of store metadata since it is possible for data archived in the metadata to be tampered with independently of the store data. If you want to ensure data security, you should use a technology such as an encrypted disk image.
There are some interactions between fetching and the type of store. In the XML, binary, and in-memory stores, evaluation of the predicate and sort descriptors is performed in Objective-C with access to all Cocoa's functionality, including the comparison methods on NSString. The SQL store, on the other hand, compiles the predicate and sort descriptors to SQL and evaluates the result in the database itself. This is done primarily for performance, but it means that evaluation happens in a non-Cocoa environment, and so sort descriptors (or predicates) that rely on Cocoa cannot work. The supported sort selectors are compare: and caseInsensitiveCompare:, localizedCompare:, localizedCaseInsensitiveCompare:, and localizedStandardCompare: (the latter is Finder-like sorting, and what most people should use most of the time). In addition you cannot sort on transient properties using the SQLite store.
There are additional constraints on the predicates you can use with the SQLite store:
You cannot necessarily translate “arbitrary” SQL queries into predicates.
Prior to Mac OS X v10.6, Core Data’s SQL store did not support the MATCHES operator (you could use the MATCHES operator to perform in-memory filtering of results returned from the store).
You can only have one to-many element in a keypath in a predicate.
For example, no toOne.toMany.toMany, or toMany.toOne.toMany type constructions (they evaluate to sets of sets). As a consequence, in any predicate sent to the SQL store, there may be only one operator (and one instance of that operator) from ALL, ANY, and IN.
- CoreData supports a noindex: (see NSPredicate documentation re: function expressions) that can be used to drop indices in queries passed to SQLite. This is done primarily for performance reasons: SQLite uses a limited number of indices per query, and noindex: allows the user to preferentially specify which indexes should not be used.
The SQLite store supports reading data from a file that resides on any type of file-system. The SQLite store does not in general, however, support writing directly to file-systems which do not implement byte-range locking. For DOS filesystems and for some NFS file system implementations that do not support byte-range locking correctly, SQLite will use "<dbfile>.lock" locking, and for SMB file systems it uses flock-style locking.
To summarize: byte-range locking file systems have the best concurrent read/write support; these include HFS+, AFP, and NFS. File systems with simplistic file locking are also supported but do not allow for as much concurrent read/write access by multiple processes; these include SMB, and DOS. The SQLite store does not support writing to WebDAV file-systems (this includes iDisk).
When Core Data saves a SQLite store, SQLite updates just part of the store file. Loss of that partial update would be catastrophic, so you may want to ensure that the file is written correctly before your application continues. Unfortunately, doing so means that in some situations saving even a small set of changes to an SQLite store can take considerably longer than saving to, say, an XML store. (For example, where saving to an XML file might take less than a hundredth of a second, saving to an SQLite store may take almost half a second. This is not an issue for XML or Binary stores—since they are atomic, there is a much lower likelihood of data loss that involves corruption of the file, especially since the writes are typically atomic and the old file is not deleted until the new has been successfully written.)
fsync command does not make the guarantee that bytes are written, SQLite sends a F_FULLFSYNC request to the kernel to ensures that the bytes are actually written through to the drive platter. This causes the kernel to flush all buffers to the drives and causes the drives to flush their track caches. Without this, there is a significantly large window of time within which data will reside in volatile memory—and in the event of system failure you risk data corruption. Core Data provides a way to control sync behavior in SQLite using two independent pragmas, giving you control over the tradeoff between performance and reliability:
synchronous controls the frequency of disk-syncing
PRAGMA synchronous FULL [2] / NORMAL [1] / OFF [0]
full_fsync controls the type of disk-sync operation performed
PRAGMA fullfsync 1 / 0
In Mac OS X v10.5, the default is 0.
The pragmas are publicly documented at http://sqlite.org/pragma.html.
You can set both pragmas using the key NSSQLitePragmasOption in the options dictionary when opening the store. The NSSQLitePragmasOption dictionary contains pragma names as keys and string values as objects, as illustrated in the following example:
NSPersistentStoreCoordinator *psc = /* assume this exists */ ; |
NSMutableDictionary *pragmaOptions = [NSMutableDictionary dictionary]; |
[pragmaOptions setObject:@"NORMAL" forKey:@"synchronous"]; |
[pragmaOptions setObject:@"1" forKey:@"fullfsync"]; |
NSDictionary *storeOptions = |
[NSDictionary dictionaryWithObject:pragmaOptions forKey:NSSQLitePragmasOption]; |
NSPersistentStore *store; |
NSError *error; |
store = [psc addPersistentStoreWithType:NSSQLiteStoreType |
configuration: nil |
URL:url |
options:storeOptions |
error:&error]; |
full_fsync by default. Since the fsync command does not make the guarantee that bytes are written, SQLite sends a F_FULLFSYNC request to the kernel. This causes the kernel to flush all buffers to the drives and causes the drives to flush their track caches. In Mac OS X v10.4, there are only two settings to control the way in which data in a SQLite-based store is written to disk. If you want to trade risk of data corruption against the time taken to save a file, you can set the defaults key com.apple.CoreData.SQLiteDebugSynchronous to one of three values:
0: Disk syncing is switched off
1: Normal
2 (The default): Disk syncing is performed via the fctl FULL_FSYNC command—a costly operation but one that guarantees data is written to disk
Important: The default behaviors in Mac OS X v10.4 an 10.5 are different. In Mac OS X v10.4, SQLite uses FULL_FSYNC by default; in Mac OS X v10.5 it does not.
© 2004, 2009 Apple Inc. All Rights Reserved. (Last updated: 2009-11-17)