Persistent Store Features
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.
Store Types and Behaviors
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.
No backing required
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.
Custom store types
In OS X v10.5 and later you can create your own atomic store types. For details, see Atomic Store Programming Topics.
In 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
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.
Fetch Predicates and Sort Descriptors
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
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 OS X v10.6, Core Data’s SQL store did not support the
MATCHESoperator (you could use the
MATCHESoperator to perform in-memory filtering of results returned from the store).
You can only have one to-many element in a key path in a predicate.
For example, no
toMany.toOne.toManytype 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
- 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.
File-systems supported by the SQLite store
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).
File Size May Not Reduce After Deleting a Record
Simply deleting a record from a SQLite store does not necessarily result in a reduction in the size of the file. If enough items are removed to free up a page in the database file, SQLite’s automatic database vacuuming will reduce the size of the file as it rearranges the data to remove that page. Similarly, the file size may be reduced if you remove an item that itself occupies multiple pages (such as a thumbnail image).
An SQLite file is arranged as a collection of pages. The data within those pages is managed via B-trees, not as simple fixed-length records. This is much more efficient for searching and for overall storage, since it allows SQLite to optimize how it stores both data and indexes in a single file, and is also the foundation of its data integrity (transaction and journaling) mechanism. However, the cost of this is that some delete operations may leave holes in the file. If you delete some data and add other data, the holes left by the deleted data may be filled by the added data, or the file may be vacuumed to compact its data, whichever SQLite considers most appropriate based on the operations you’re performing.
Configuring a SQLite Store’s Save Behavior
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.)
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:
synchronouscontrols the frequency of disk-syncing
PRAGMA synchronous FULL  / NORMAL  / OFF 
full_fsynccontrols the type of disk-sync operation performed
PRAGMA fullfsync 1 / 0
In OS X v10.5, the default is
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 = <#Get a persistent store coordinator#>;
NSMutableDictionary *pragmaOptions = [NSMutableDictionary dictionary];
[pragmaOptions setObject:@"NORMAL" forKey:@"synchronous"];
[pragmaOptions setObject:@"1" forKey:@"fullfsync"];
NSDictionary *storeOptions =
[NSDictionary dictionaryWithObject:pragmaOptions forKey:NSSQLitePragmasOption];
NSError *error = nil;
store = [psc addPersistentStoreWithType:NSSQLiteStoreType