Realm database from entry to “give up””

Realm database from entry to

Preface

Since the recent projects in the use of Realm, so some of their own experience in the process of practice summed up to share.

Realm is an open source team launched by Y Combinator incubation, a cross platform mobile database that can be used in iOS (also applicable to Swift&amp, Objective-C) and Android. The latest version is Realm 2.0.2, supported by platforms including Java, Objective-C, Swift, React, Native, and Xamarin.

Realm has a lot of advantages on its website. I think the most attractive advantage of using Realm is three points:

  1. Cross platform: now a lot of applications are to take into account both iOS and Android two platforms simultaneously developed. If the two platforms can use the same database, then there is no need to consider the architecture of the internal data differently. Using the Realm provided by API, the data persistence layer can be transformed without differentiation on the two platforms.
  2. Easy to use: Core Data and SQLite redundancy, complex knowledge and code to scare most entry-level developers, but for Realm, it can greatly reduce the cost of learning methods, immediately learn local storage. Without boasting, the latest official documents can be fully developed.
  3. Visualization: Realm provides a lightweight database view tool in Mac Appstore can download the “Realm Browser” this tool, developers can view the content of the database, performs a simple operation to insert and delete data. After all, many times, developers use the database because they want to provide some so-called “knowledge base””.
Realm database from entry to

“Realm Browser” this tool, debugging Realm database is very useful, strongly recommended.

If you use the simulator for debugging, you can pass

[RLMRealmConfiguration defaultConfiguration].fileURL

Print out the Realm address of the database, and then in the Finder G jump to the corresponding path, open the corresponding.Realm file with Realm Browser you can see the data.

If you are using a real machine debugging words “Xcode-> Window-> Devices (2)”, and then find the corresponding equipment project, click Download Container, export xcappdata file, display the contents of the package, into AppData-> Documents, using Realm Browser open the.Realm file.

Since 2012, Realm has begun to be used in formal commercial products. After 4 years of use, gradually stabilized.

Catalog

  • 1.Realm installation
  • Related terms in 2.Realm
  • Introduction to 3.Realm – how to use
  • Some problems that may need to be noticed in the use of 4.Realm
  • 5.Realm “give up” – strengths and weaknesses
  • What the hell is 6.Realm?
  • 7. summary

I. installation of Realm

Basic requirements for building applications using Realm:

  1. IOS 7 and above, macOS 10.9 and above, and Realm supports all versions of tvOS and watchOS.
  2. You need to use Xcode 7.3 or later.

Note that if this is a pure OC project, install OC’s Realm and, if it’s a pure Swift project, install Swift’s Realm. If the project is mixed, you will need to install the OC Realm, and then to the Swift/RLMSupport.swift file to compile in.

The file RLMSupport.swift Sequence bringconsistency collection type for the Objective-C version of the Realm, and re exposed some can not be accessed from the Swift Objective-C in the original method, such as variable parameters (variadic arguments). See the official documentation for more details.

There are 4 ways to install:

One. Dynamic Framework

Note: the dynamic framework is not compatible with iOS 7. To support iOS 7, see the static framework”.

  1. Download the latest Realm release and unzip it;
  2. Go to the “General” setting item in the Xcode project, drag “Realm.framework” from the ios/dynamic/, osx/, tvos/,
    , or watchos/ to the “Embedded Binaries” option. After you confirm that Copy items if needed is selected, click the Finish button;
  3. In the unit test Target “Build Settings”, add the Realm.framework’s higher directory in “Framework Search Paths”;
  4. If you want to load Realm with Swift, drag the Swift/RLMSupport.swift
    file into the file navigation bar of the Xcode project and select Copy items if needed;
  5. If you use Realm in iOS, watchOS, or tvOS projects, create a new “Run Script Phase” in your application’s “Build Phases”, and
Bash "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/Realm.framework/strip-frameworks.sh""

This script is copied into the text box. This step is necessary to package the binary version of the general purpose device because you want to bypass the bug submitted by the APP store.

Two.CocoaPods
Realm database from entry to

In the Podfile of the project, add pod’Realm’and run pod install at the terminal.

Three.Carthage

1. add GitHub “realm/realm-cocoa” in Carthage and run Carthage update. To modify the Swift toolchain for building the project, specify the appropriate toolchain through the –toolchain parameter. The –no-use-binaries parameter is also required, which prevents Carthage from downloading the pre built Swift 3 binary package. Such as:

Carthage, update, --toolchain, com.apple.dt.toolchain.Swift_2_3, --no-use-binaries

2. from the Carthage/Build/ directory, in the corresponding platform folder, drag Realm.framework
to your Xcode project “General” to set the item “Linked Frameworks and Libraries” tab;

3.iOS/tvOS/watchOS: in the Build Phases Settings tab of your application’s target, click the + button, and select New Run Script Phase”. In the new Run Script, fill in:

/usr/local/bin/carthage copy-frameworks

Add the frame path you want to use in Input Files, for example:

$(SRCROOT) /Carthage/Build/iOS/Realm.framework

This step is necessary to package the binary version of the general purpose device because you want to bypass the bug submitted by the APP store.

Four.Static Framework (iOS only)
  1. Download the latest version of Realm and unzip it and drag Realm.framework from the ios/static/ folder to the file navigator in your Xcode project. Make sure Copy items if needed is checked, and then click Finish;
  2. In the Xcode file navigator, select your project, and then select your application target and go to the Build Phases tab. In Link Binary with Libraries, click the + number, and then add libc++.dylib;

Two. Related terms in Realm

In order to better understand the use of Realm, let’s first introduce some related terms.

RLMRealm:Realm is the core of the framework, which is the access point for building the database, just like the management object context of Core Data (managed object context). For simplicity’s sake, realm provides a convenient constructor method for the default defaultRealm ().

RLMObject: This is our custom Realm data model. The behavior of creating a data model corresponds to the structure of the database. To create a data model, we just need to inherit RLMObject and design the properties we want to store.

Relationship (Relationships): by simply declaring a RLMObject type attribute in a data model, we can create a one to many object relationship. Similarly, we can also create “many to one” and “many to many” relationships.

Write Transactions: all operations in a database, such as creating, editing, or deleting objects, must be done in the transaction. “Transaction” refers to the code segment in the write closure.

Query (Queries): to retrieve information in a database, we need to use the “retrieve” operation. The simplest form of retrieval is to send query messages to the Realm () database. If you need to retrieve more complex data, you can also use assertions (predicates), composite queries, and result sorting, and so on.

RLMResults: this class is the class returned after any query request is executed, which contains a series of RLMObject objects. Like NSArray and RLMResults, we can access them by subscript syntax, and we can also determine their relationships. Not only that, it also has many more powerful functions, including sorting, searching, and so on.

Three.Realm introduction – how to use

Because Realm’s API is very friendly, I can understand it at a glance, so I sort out what I need to use in order to develop it.

1. create database
- (void) creatDataBaseWithName: (NSString * databaseName) {NSArray *docPath = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES); NSString *path = [docPath objectAtIndex:0]; NSString *filePath = [path stringByAppendingPathComponent:databaseName]; NSLog (@ database directory =% @ ", filePath); RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration]; config.fileURL [NSURL = URLWithString:filePath]; config.objectClasses = @ [MyClass.class. MyOtherClass.class] config.readOnly; int = NO; currentVersion = 1; config.schemaVersion = currentVersion; config.migrationBlock = (RLMMigration ^ *migration, uint64_t oldSchemaVersion) {/ / this is the data set Migrating block if (oldSchemaVersion < currentVersion) {}}; [RLMRealmConfiguration setDefaultConfiguration:config];}

Create a database, the main settings RLMRealmConfiguration, set the database name and storage place. The path and database name splicing good string, assigned to fileURL can.

The objectClasses property is used to control which class can be stored in the specified Realm database. For example, if the two teams are responsible for the different part of the development of your application, and also used in the application of the Realm database, then you certainly don’t want them to coordinate the data migration through objectClasses you can set the RLMRealmConfiguration property to make the restrictions on the class. ObjectClasses can generally not be set.

ReadOnly controls whether read-only attributes are controlled.

There is also a very special database, memory database.

Typically, the Realm database is stored in the hard disk, but you can set up through the inMemoryIdentifier and is not set in the RLMRealmConfiguration property of the fileURL, to create a complete run in memory database.

RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration]; config.inMemoryIdentifier = @ MyInMemoryRealm '; RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];

The memory database does not save data during each program run. However, this does not interfere with other functions of Realm, including queries, relationships, and thread safety.

If you need a flexible way to read and write data, but you don’t want to store data, you can choose to use the memory database. (the performance of the memory database and class properties has not been tested yet, and the sensory performance doesn’t vary much, so memory databases don’t use much of the scene)

What you need to pay attention to using memory databases is that:

  1. The memory database creates multiple files in the temporary folder to coordinate transactions such as cross process notifications. In fact, no data will be written into these files unless the operating system needs to clear up the excess space on the disk because memory is full. Will go to the memory of the data into the file. (thanks to @ cool mourning hall.)
  1. If a memory Realm database instance is not referenced, all the data is freed. Therefore, you must maintain a strong reference to the Realm memory database during the life of the application to avoid data loss.
2. build table

The Realm data model is based on the standard Objective C class to define the specific definition of attributes are used to complete the model.

We just need to inherit RLMObject or an existing model class, so you can create a new Realm data model object. Corresponding to the database is a table.

#import < Realm/Realm.h> @interface; RLMUser: RLMObject @property NSString *accid ID @property NSInteger; / / registered user name @property NSString custId; / / *custName / / url @property NSString head; *avatarBig @property; RLMArray< Car> *cars; RLM_ARRAY_TYPE (RLMUser) RLMArray< RLMUser> @interface; / / definition; Car: RLMObject @property NSString *carName; @property RLMUser *owner; @end RLM_ARRAY_TYPE (Car) RLMArray< Car> @end; / / definition;

Note that the official RLMObject is not recommended with Objective-C property attributes (nonatomic, atomic, strong, copy, weak and so on) if set, the attributes will remain in effect until the RLMObject is written to the realm database.

The RLM_ARRAY_TYPE macro creates a protocol that allows RLMArray< Car> the use of syntax. If the macro is not placed at the bottom of the model interface, you may need to declare the model class ahead of time.

About RLMObject’s relationship

1. to one (To-One) relationship

For many to one (many-to-one) or one to one (one-to-one) relationships, you only need to declare properties of a RLMObject subclass type, such as the code example above, @property, RLMUser, *owner;

2. to many (To-Many) relationships,
, you can define a pair of relationships through the attributes of the RLMArray type. As in the above code example, @property RLMArray< Car> *cars;

3. reverse relation (Inverse, Relationship)

Links are unidirectional. Therefore, if the relationship between the properties of the RLMUser.cars link to a Car instance, and the instance of a relationship between the Car.owner attribute and link to the corresponding RLMUser instance, then in fact these links are still independent of each other.

@interface Car: RLMObject @property NSString *carName @property (readonly); RLMLinkingObjects *owners; @end @implementation Car + (NSDictionary * linkingObjectsProperties) {return @{@ "owners" [RLMPropertyDescriptor descriptorWithClass:RLMUser.class propertyName:@ "cars"]}}; @end

Here can be analogy Core Data inside xcdatamodel file inside those “arrow””

Realm database from entry to
@implementation Book / / Key + (NSString *) primaryKey return {@ "ID";} / / set the default value for an attribute + (NSDictionary *) defaultPropertyValues{return @{@ "carName": @ "test"};} / / setting ignores attributes, which does not exist in the realm database (NSString + NSArray< *> * ignoredProperties) @[@ {return "ID"];} / / general properties for the nil if realm will throw an exception, but if the realization of this method, only name nil will throw an exception, that is now the cover attributes can be empty + (NSArray *) requiredProperties @[@ {return "name"]}; / / set index, can speed up the retrieval speed + (NSArray * indexedProperties) {return} @[@ "ID"]; @end

You can also set the primary key primaryKey to the RLMObject, the default value defaultPropertyValues, the attribute ignoredProperties ignored, the necessary attribute requiredProperties, the index indexedProperties. More useful are primary keys and indexes.

3. storing data

New object

/ / (1) to create a Car object, and then set the properties of the Car *car [[Car alloc] = init]; car.carName = @ "Lamborghini" (2); / / create a Car Car *myOtherCar [[Car alloc] initWithValue:@{@ object = "name" through the dictionary: @ "Rolls-Royce"}]; / / (3) = [[Car *myThirdcar Car to create a dog object alloc] "BMW" initWithValue:@[@ "through the array;

Note that all required attributes must be assigned before the object is added to Realm

4.

Realm database from entry to
[realm beginWriteTransaction]; [realm addObject:Car]; [realm commitWriteTransaction];

Note that if there are multiple write operations in the process, then a single write operation will block the rest of the write operation and lock the current thread in which the operation is located.

The Realm feature is similar to other persistence solutions, and we recommend that you use the general best practice of this scenario: to transfer write operations into a separate thread.

The official gave a suggestion:

Since Realm uses the MVCC design framework, the read operation is not affected by writing the transaction in progress. Unless you need to immediately use multiple threads to perform write operations concurrently, you should write batch transactions instead of using a small amount of write transactions.

Dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{RLMRealm *realm = [RLMRealm defaultRealm]; [realm transactionWithBlock:^{[realm addObject: Car];}];});

The above code is to write the transaction into the child thread.

5 deleted
Realm database from entry to
[realm beginWriteTransaction] [realm deleteObject:Car]; / / delete individual records; / / delete multiple records [realm deleteObjects:CarResult]; / / delete all records [realm deleteAllObjects]; [realm commitWriteTransaction];
6 change
Realm database from entry to

When there are no primary keys, you need to query first and then modify the data.
when you have a primary key, there are several very useful API

[realm addOrUpdateObject:Car]; [Car createOrUpdateInRealm:realm withValue:@{@ "Id": @1, @ price ': @9000.0f}];

AddOrUpdateObject will go to find the same primary key that has no incoming Car, and if so, update the bar data. It should be noted that the addOrUpdateObject method is not incremental update, all values must have, if there is some value is null, then the original cover has some value, so there will be the problem of data loss.

CreateOrUpdateInRealm:withValue: this method is incrementally updated, followed by a dictionary, using this method on the premise that there is a primary key. The method will first go to the primary key to find the record of the primary key inside the dictionary, and if so, just update the subset inside the dictionary. If not, create a new record.

7 check
Realm database from entry to

In Realm, all queries (including query and attribute access) are deferred loaded in Realm, and only when the property is accessed can the corresponding data be read.

The result of the query is not a copy of the data: modifying the query results (in the transaction) will directly modify the data on the hard disk. Similarly, you can complete the operation of the traversal diagram directly through the RLMObject object contained in the RLMResults. Retrieval execution will be deferred unless the query results are used. This means linking several different temporary {RLMResults} for sorting and matching data without performing additional work, such as handling intermediate states.
once the retrieval is executed, or the notification module is added, the RLMResults will always be updated to receive changes that may be made in the retrieval operations performed in the Realm thread on the background thread.

All the cars RLMResults< / / query from the default database; Car *> *cars [Car = allObjects]; / / use predicate string query RLMResults< Dog *> *tanDogs = [Dog objectsWhere:@ color = 'AND name BEGINSWITH' Brown 'big' "]; / / NSPredicate NSPredicate *pred [NSPredicate predicateWithFormat:@ query =" color = AND name% @ BEGINSWITH "Brown,"% @ @ @ "," big "]; RLMResults *results = [Dog objectsWithPredicate:pred]; / / sort name with" big "at the beginning of the brown dog RLMResults< Dog *> *sortedDogs = [[Dog objectsWhere:@ color = 'AND name BEGINSWITH' Brown 'big' sortedResultsUsingProperty:@" name "ascending:YES]"];

Realm also supports chain queries

The Realm query engine of a characteristic is that it can be through the transaction very little overhead to perform query chain (chain queries), without the need to create a different database server access like traditional database as each successful query.

RLMResults< Car *> *Cars = [Car, objectsWhere:@, color = blue]; RLMResults< Car *> *CarsWithBNames = [Cars, objectsWhere:@, name, BEGINSWITH,'B'];
8. other relevant features

1. support KVC and KVO

RLMObject, RLMResult, and RLMArray
follow the key encoding (Key-Value, Coding) (KVC) mechanism. This method is most useful when you are running to determine which properties need to be updated.
applies KVC to a collection, which is an excellent way to update objects in large numbers so that you can create an accessor for each project without going through the collection frequently.

RLMResults< Person *> *persons = [Person allObjects]; [[RLMRealm defaultRealm] transactionWithBlock:^{[[persons firstObject] setValue:@YES forKeyPath:@ "isFirst"]; / / the planet property of each person is set to "Earth" [persons setValue:@ "Earth" forKeyPath:@ "planet"];}];

Most properties of the Realm object follow the KVO mechanism. All subclasses of RLMObject persistence (persisted) storage (not be ignored) attributes are to follow the KVO mechanism, and the invalid RLMObject as well as RLMArray in the (invalidated) attributes also follow (whereas RLMLinkingObjects attribute does not use KVO for observation).

2. support database encryption

The random key NSMutableData / / *key = [NSMutableData dataWithLength:64]; SecRandomCopyBytes (kSecRandomDefault, key.length, key.mutableBytes (uint8_t *)); / / open RLMRealmConfiguration *config defaultConfiguration] encrypted file = [RLMRealmConfiguration; config.encryptionKey = key; NSError = *error nil; RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:& error]; if (realm!) {/ / if the key error, `error` that database is not accessible ("Error opening realm: NSLog @% @", error);}

Realm supports the AES-256+SHA2 encryption of database files with a 64 bit key when creating Realm databases. In this way, all the data on the hard disk can be encrypted and decrypted by AES-256 and verified by SHA-2 HMAC. Each time you want to obtain a Realm instance, you need to provide the same key once.

However, encrypted Realm only brings a small amount of extra resources (usually only 10% slower than usual).

3. notice

Realm = [realm token / / get the notice of addNotificationBlock:^ (NSString *notification, RLMRealm * realm) {[myViewController updateUI];}]; [token stop]; / / [realm removeNotification:self.token] removal notice;

The Realm instance will send notifications to Realm instances on other threads after each transaction transaction is committed. If the general controller wants to hold this notification all the time, you need to apply for an attribute, and strong holds the notification.

- (void) viewDidLoad {[super viewDidLoad]; / / RLMResults __weak typeof notice observation (self) weakSelf = self; self.notificationToken = [[Person objectsWhere:@ age > 5] addNotificationBlock:^ (RLMResults< Person *> *results, RLMCollectionChange *change, NSError *error) {if (error) {NSLog ("Failed to open Realm on @ background worker:, error; return% @");} UITableView *tableView = weakSelf.tableView; / / to change information, the initial operation will transfer the nil if retrieval (! Changes) {[tableView reloadData]; return;} / / the retrieval result is changed, so they are applied to UITableView [tableView beginUpda Tes]; [tableView deleteRowsAtIndexPaths:[changes deletionsInSection:0] withRowAnimation:UITableViewRowAnimationAutomatic]; [tableView insertRowsAtIndexPaths:[changes insertionsInSection:0] withRowAnimation:UITableViewRowAnimationAutomatic] tableView reloadRowsAtIndexPaths:[changes modificationsInSection:0] withRowAnimation:UITableViewRowAnimationAutomatic] "; [tableView; endUpdates];}];}

We also have finer grained notifications that can be done with a collection notification.

A collection notification is triggered asynchronously. First, it triggers when the initial result appears, and then, when a transaction is written to change all or some object in the collection, the notification triggers again. These changes can be accessed by passing the RLMCollectionChange parameter to the notification closure. This object contains index information that is affected by the deletions, insertions, and modifications states.

Set for RLMResults, RLMArray, inform RLMLinkingObjects and RLMResults the derived set, when the object of the relation is added or deleted, will trigger the change of state.

4. database migration

This is one of the advantages of Realm for easy migration.

Comparing the data migration of Core Data is too convenient. For the iOS Core Data data migration guide, read this article.

Database storage, additions and deletions to change the search should be no big problem, compared to the egg, it should be the data transfer. In the version iteration process, it is very likely that new additions, deletions, or changes in table structure will occur, and if the new version does not do data migration, the user updates to the new version and is likely to go straight to crash. Comparing Core and Data data migration is complicated, and the migration of Realm is too simple.

1. new delete table, Realm do not need to do migration,
2. new delete field, Realm do not need to do migration. Realm will automatically detect new and removed properties, and then automatically update the database schema on the hard disk.

Give an example of official data migration:

RLMRealmConfiguration *config [RLMRealmConfiguration = defaultConfiguration]; config.schemaVersion = 2; config.migrationBlock = (RLMMigration ^ *migration, uint64_t oldSchemaVersion) {/ / enumerateObjects:block: traversal was stored in the Realm file for each "Person" object [migration enumerateObjects:Person.className block:^ (RLMObject *oldObject, RLMObject *newObject) {/ / only when the Realm database schema version 0, add the "fullName" attribute if (oldSchemaVersion < 1) {newObject[@ "fullName"] = [NSString stringWithFormat:@ oldObject[@ "% @% @", "firstName", "lastName" oldObject[@ ";} / / only when the Realm database schema version for 0 or 1, add" EM Ail if (oldSchemaVersion < attribute 2) {newObject[@ "email"] = "@";} / / replace the property name if (oldSchemaVersion < 3) {/ / rename operation should be performed outside the [migration renamePropertyForClass:Person.className call `enumerateObjects:` "yearsSinceBirth" oldName:@ newName:@ "age"];}};}]; [RLMRealmConfiguration / setDefaultConfiguration:config]; now we have successfully updated the schema version and provides the transfer closure, the old open Realm database will automatically perform this data migration, and then successfully access [RLMRealm defaultRealm];

There are 3 migration methods in block, the first is the example of merging fields, the second is the example of adding new fields, and the third is the example of renaming the original field.

Four. Some problems that Realm may need to pay attention to in use

Realm database from entry to

In my contact with Realm from 0 to skilled, basically encountered a hole in the multi thread. Obviously, Realm’s API document is so friendly. Although the pit is not many, but there are some places to pay attention to.

1., cross thread access database, Realm object must be a new one
App due to uncaught exception * * * Terminating'RLMException', reason:'Realm accessed from incorrect thread.'** First throw call stack:** * * * * * * * * * * (0 CoreFoundation 0x000000011479f34b __exceptionPreprocess 1 libobjc.A.dylib 0x00000001164a321e + 171** * * objc_exception_throw + 48** * * 2 BHFangChuang 0x000000010dd4c2b5 -[RLMRealm beginWriteTransaction] BHFangChuang 0x000000010dd4c377 -[RLMRealm 3 + 77** * * transactionWithBlock:error:] + 45** * * 4 BHFangChuang 0x000000010dd4c348 - RLMRealm transactionWithBlock:] + 19** * * 5 BHFangChuang 0x000000010d51d7ae __71-[RealmDataBaseHelper updateUserWith LoginDate:andLogoutDate:according:]_block_invoke + 190** 6 libdispatch.dylib + 12** _dispatch_call_block_and_release 0x00000001180ef980 * * * * libdispatch.dylib 0x00000001181190cd 7 _dispatch_client_callout + 8** 8 libdispatch.dylib + 1426** _dispatch_queue_override_invoke 0x00000001180f8366 * * * * 9 libdispatch.dylib 0x00000001180fa3b7 _dispatch_root_queue_drain 10 libdispatch.dylib 0x00000001180fa08b + 720** * * * * _dispatch_worker_thread3 + 123** 11 libsystem_pthread.dylib 0x00000001184c8746 _pthread_wqthread 12 libsystem_pthread.dylib 0x00000001184c8221 + 1299** * * start_wqthread + 13** * * * * **libc++abi.dylib: term) Inating, with, uncaught, exception, of, type, NSException**

If the program crashes, the error appears, because the Realm object you are using is inconsistent with the current thread when you access the Realm data.

The solution is to retrieve the latest Realm at the current thread.

2., packaging a Realm global instance, single case is no function

This is also my previous understanding of multi-threaded Realm, resulting in a misunderstanding.

Many developers should encapsulate a single example of Helper for Core, Data, and Sqlite3 or FMDB. So here I encapsulate a single instance, when the new Realm database is completed, strong holds a Realm object. Then after the visit, you only need to read the Realm object held by the singleton, so you can get the database.

The idea is good, but the same Realm object does not support cross thread manipulation of the realm database.

Realm makes it easy to run concurrently by ensuring that each thread has a snapshot of Realm all the time. You can access the same Realm file with any number of threads at the same time, and since each thread has a corresponding snapshot, there is no impact between threads. One thing to note is that you can’t let multiple threads hold an instance of the same Realm object. If multiple threads need to access the same object, then they will get their own examples respectively need (or will change in a thread caused by other threads are incomplete or inconsistent data).

In fact, “RLMRealm *realm = [RLMRealm defaultRealm]”; this sentence is to obtain an instance of the current realm object, in fact, is to get a single example. So every time we go inside the child thread, don’t read the realm instance we hold in our own package. This method can be called directly to ensure access without error.

3.transactionWithBlock is already in a written transaction that cannot be nested between transactions
[realm transactionWithBlock:^{[self.realm beginWriteTransaction] [self convertToRLMUserWith:bhUser To:[self convertToRLMUserWith:bhUser To:nil]]; [self.realm; commitWriteTransaction];}];

TransactionWithBlock is already in a written transaction, and if you write another commitWriteTransaction in block, you’ll make a mistake and write a transaction that cannot be nested.

Error messages are as follows:

App due to uncaught exception * * * Terminating'RLMException', reason:'The Realm is already in a write transaction'** First throw call stack:** * * * * * * * * * * (0 CoreFoundation 0x0000000112e2d34b __exceptionPreprocess 1 libobjc.A.dylib 0x0000000114b3121e + 171** * * objc_exception_throw + 48** * * 2 BHFangChuang 0x000000010c4702b5 -[RLMRealm beginWriteTransaction] BHFangChuang 0x000000010bc4175a __71-[RealmDataBaseHelper 3 + 77** * * updateUserWithLoginDate:andLogoutDate:according:]_block_invoke_2 + 42** * * 4 BHFangChuang 0x000000010c470380 -[RLMRealm transactionWithBlock:error:] + 54** * * 5 BHFangChuang 0x000000010c470348 -[RLMRealm transactionWithBlock:] BHFangChuang 0x000000010bc416d7 __71-[RealmDataBaseHelper 6 + 19** * * updateUserWithLoginDate:andLogoutDate:according:]_block_invoke + 231** * * 7 libdispatch.dylib 0x0000000116819980 _dispatch_call_block_and_release + 12** 0x00000001168430cd + 8** _dispatch_client_callout libdispatch.dylib * * 8 * * 9 libdispatch.dylib 0x0000000116822366 _dispatch_queue_override_invoke 10 libdispatch.dylib 0x00000001168243b7 + 1426** * * * * _dispatch_root_queue_drain + 720** 11 libdispatch.dylib 0x000000011682408b _dispatch_worker_thread3 + 123** * * 12 libsystem_pthread.dylib 0x0000000116bed746 _pthread_wqthread + 1299** 13 libsystem_pthread.dylib + 13** start_wqthread 0x0000000116bed221 * * * * * * **libc++abi.dylib: terminating with uncaught exception) of type NSException**
4. it is recommended that each model needs to set the primary key so that it is convenient for add and update

If you can set the primary key, please try to set the primary key, because it is convenient for us to update the data, we can easily call addOrUpdateObject: or createOrUpdateInRealm:withValue: update method. This way you don’t have to query the data from the primary key first and then update it. After you have the primary key, the two steps can be done one step at a time.

5. queries cannot cross thread queries
RLMResults * results = [self selectUserWithAccid:bhUser.accid]; dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{RLMRealm *realm = [RLMRealm defaultRealm] [realm; transactionWithBlock:^{[realm addOrUpdateObject:results[0]];}];});

Because queries are queries outside the child thread, cross threads can also make errors, and the error messages are as follows:

A Terminating app due to uncaught exception'RLMException', reason:'Realm accessed from incorrect thread'** First throw call stack:** * * * * * * * * * * (0 CoreFoundation 0x000000011517a34b __exceptionPreprocess 1 libobjc.A.dylib 0x0000000116e7e21e + 171** * * objc_exception_throw + 48** * * 2 BHFangChuang 0x000000010e7c34ab _ZL10throwErrorP8NSString 3 BHFangChuang 0x000000010e7c177f -[RLMResults + 129** * * count] + 40** * * 4 BHFangChuang 0x000000010df8f3bf -[RealmDataBaseHelper convertToRLMUserWith:LoginDate:LogoutDate:To:] BHFangChuang 0x000000010df8efc1 + 159** * * 5 __71-[RealmDataBaseHelper upda TeUserWithLoginDate:andLogoutDate:according:]_block_invoke_2 + 81** 6 BHFangChuang + transactionWithBlock:error:] -[RLMRealm 0x000000010e7bd320 * * 54** * * 7 BHFangChuang 0x000000010e7bd2e8 -[RLMRealm transactionWithBlock:] BHFangChuang 0x000000010df8eecf __71-[RealmDataBaseHelper 8 + 19** * * updateUserWithLoginDate:andLogoutDate:according:]_block_invoke + 351** * * 9 libdispatch.dylib 0x0000000118b63980 _dispatch_call_block_and_release + 12** 0x0000000118b8d0cd + 8** _dispatch_client_callout libdispatch.dylib * * 10 * * 11 libdispatch.dylib 0x0000000118b6c366 _dispatch_queue_override_invoke + 1426** * * 12 libdi Spatch.dylib 0x0000000118b6e3b7 _dispatch_root_queue_drain libdispatch.dylib 0x0000000118b6e08b _dispatch_worker_thread3 13 + 720** * * * * libsystem_pthread.dylib 0x0000000118f3c746 + 123** 14 _pthread_wqthread + 1299** 15 libsystem_pthread.dylib + 13** start_wqthread 0x0000000118f3c221 * * * * * * **libc++abi.dylib: terminating with uncaught) exception of type * *

Five. Realm “give up” – advantages and disadvantages

About the advantages of Realm, in the official website also said a lot of, I feel the deepest 3 advantages also mentioned in the article at the beginning.

CoreData VS Realm contrast, you can read this article

Realm database from entry to

When it comes to using the last two thresholds of Realm, one is how to migrate from other databases to Realm, and the other two is some restrictions on Realm databases.

Next, please consider whether to use Realm’s classmates to look carefully. Here are the important criteria you need to weigh whether you want to change to the Realm database. (the following description is based on the latest version of 2.0.2 Realm)

1. migrate from other databases to Realm
Realm database from entry to

If the migration from other database to Realm, please see the article I wrote before, provided a simple eggs pain problem, because the switch of the database, several versions must be needed in the future to maintain 2 sets of database, because the old user data from the old database need to slowly migrate to Realm, this is a little egg the pain. The code that migrates the data needs to be “sick” in the works. But once the migration is complete, the road is smoother.

Regarding the Core Data migration, there is no fetchedResultController problem here. Since the Core Data fetchedResultController cannot be used because Realm is used, then if the database updates the data, is it possible to update the tableview only through reloadData? Now basically is, Realm provides our notification mechanism, current Realm add notifications to the realm database object, so that you can write after the transaction commits to obtain in the database, so as to update the details can refer to https://realm.io/cn/docs/swift/latest/# notification UI; of course if you still want to use NSFetchedResultsController, then recommend the use of RBQFetchedResultsController, which is an alternative at present, the address is: https://github.com/Roobiq/RBQFetchedResultsController Realm plan in the future to achieve a similar effect, you can see the specific PR:http://github.com/realm/realm-cocoa/issues/687.

Of course, if the new App is still in development, you can consider direct use of Realm, will be more comfortable.

The above is the first threshold, if you think the transfer can bring the price to bear, then congratulations, you have entered the Realm half. Then, please look at the second threshold”.

2. Realm database current version limit

The part of the user is still stopped at the door of the Realm the second threshold, because of these limitations, these shortcomings, leading to the business of App Realm cannot be used to satisfy, so finally gave up Realm. Of course, some of these problems can be flexibly solved by changing the table structure, after all, people are alive (if you really want to use Realm, think of some ways, no one can stop it)

Realm database from entry to

The 1. class name has the largest length, and only 57 UTF8 characters can be stored.

The maximum length of the 2. property name can only support 63 UTF8 characters.

The 3.NSData and NSString properties cannot store data over 16 MB in size. If you want to store a large amount of data, you can break it down into 16MB sized blocks, or store it directly in a file system, and store the file path in Realm. If your application attempts to store a single attribute greater than 16MB, the system throws an exception at run time.

4 string sort, case insensitive queries only support the “Latin character set”, “Latin character set” and “Latin extended character set A” and “B” (Latin extended character set UTF-8 in the range of 0~591).

5., although Realm files can be accessed by multiple threads at the same time, you can’t process Realms, Realm objects, queries, and query results across threads. This is not really a problem. We can create new Realm objects in multithreading

Setters &amp of the 6.Realm object; Getters cannot be overloaded

Because Realm overrides the setters and getters methods in the underlying database, you can’t overwrite it on your object. A simple alternative is to create a new Realm that ignores attributes, access to the property can be overridden, and other getter and setter methods can be invoked.

7. file size & version tracking

Generally speaking, the Realm database takes less space than the SQLite database on the hard disk. If your Realm file size is beyond your imagination, this may be because the RLMRealm in your database contains old version data.
in order to make your data display the same way, Realm updates the data version only at the beginning of the loop iteration. This means that if you read some of the data from the Realm and the long time operation in a locked thread, then read and write Realm database in other threads, then the version will not be updated, Realm will save the intermediate version of the data, but these data are not used. This leads to the increase of file size. This part of the space will be reused during the next write operation. These operations can be implemented by calling writeCopyToPath:error.

The solution:
calls invalidate to tell Realm that you don’t need the data copied to Realm anymore. This allows us to keep track of the intermediate versions of these objects. Next time the new version appears, version update is done.
you might have discovered this problem when using Grand Central Dispatch at Realm. When the dispatch queue (dispatch queue) is released automatically after the end of dispatch, the queue (dispatch) is not released along with the program. This causes the Realm intermediate version of the data space to be re exploited until the
RLMRealm object is released. To avoid this problem, you should use an explicit queue (dispatch) in the dispatch queue.

8.Realm does not have automatic growth properties

Realm has no thread / process security auto growth property mechanism, which is often used to generate primary keys in other databases. However, in most cases, for primary keys, what we need is a unique, automatically generated value, so there is no need to use sequential, contiguous, and integer ID as primary keys.

Terms of settlement:

In this case, a unique string primary key usually meets the requirements. A common pattern is to set the default property value to [[NSUUID UUID] UUIDString]
to produce a unique string called ID. Another common motivation for
automatic growth attributes is to maintain order after insertion. In some cases, this can be done by adding objects to a RLMArray, or using the createdAt attribute of the [NSDate date] default value.

9. all data models must be directly inherited from RealmObject. This prevents us from exploiting any type of inheritance in the data model.

This is not a problem. We can solve this problem as long as we build a model. Their own model, you can free to inherit, this model specifically used to receive network data, and then put their own model into the table to be stored inside the model, that is, RLMObject objects. Thus, the problem can be solved.

Realm allows models to generate more subclasses, and also allows code reuse across models, but because of certain Cocoa characteristics, rich class polymorphism at runtime is unavailable. Here are some of the things that can be done:

  • Class methods, instances, methods, and attributes in the parent class can be inherited by its subclasses
  • In subclasses, you can use the parent class as arguments in methods and functions

The following is not completed:

  • Conversions between polymorphic classes (for example, subclasses are converted into subclasses, subclasses are converted into parent classes, parent classes are converted into subclasses, and so on)
  • Multiple classes are retrieved at the same time
  • Multi class containers (RLMArray and RLMResults)

10.Realm does not support collection types

This is also more than the egg pain.

Realm supports the following property types: BOOL, bool, int, NSInteger, long, long, long, float, double, NSString, NSDate, NSData, and NSNumber that are marked by a particular type. The support for the CGFloat property has been canceled because it does not have platform independence.

This is where collections are not supported, such as NSArray, NSMutableArray, NSDictionary, NSMutableDictionary, NSSet, NSMutableSet. If a dictionary comes from the server, key is a string, and the corresponding value is an array, and it is difficult to store the array at this time. Of course, there is a collection of Realm, that is, RLMArray, which is loaded with RLMObject.

So we want to solve this problem, you need to put things inside the data are taken out, if model, will receive their first, and then converted to RLMObject model, and then stored in the RLMArray inside, this conversion can still be done again.

Here is a list of the current Realm measures are temporarily “faults”, if these 10 points, can meet the needs of the business on its own App, so this test is not the problem.

Two please carefully measure the clear cut, there is also an article is the experience on replacing the database experience, change the tire for highway legacy systems replacement database consider replacing the students can also see. The two hurdle if it does not fit, not to go, so please give up Realm!

Six. What exactly is Realm?

Realm database from entry to

As you all know, Sqlite3 is a small database used on a mobile terminal, and FMDB is a package based on Sqlite3.

Is the Core Data database?
Core Data itself is not a database. It is a framework with many functions. One of the important functions is to automate the interaction process between applications and databases. With the Core Data framework, we don’t have to write Objective-C code, or we can use relational databases. Because Core Data automatically generates the SQL statement that should be optimally optimized at the bottom.

So, Realm is the database?

Realm database from entry to

Realm is not ORM, nor is it based on SQLite creation, but rather a full-featured database for mobile developers. It can map native objects directly to the Realm database engine (much more than just a key to memory).

Realm is a MVCC database, and the bottom layer is written in C++. MVCC refers to versioning concurrency control.

Realm meets ACID. Atomicity (Atomicity), consistency (Consistency), isolation (Isolation), persistence (Durability). A database that supports transactions (Transaction) must have these four characteristics. Realm has been met.

1.Realm uses the design concept of MVCC

MVCC solves an important problem: there are complicated when all the database, when someone is writing a database to read the database (for example, different threads can be read or written at the same database). This can lead to inconsistencies in data – possibly when a write operation is partially ended when you read the record.

There are many ways to solve the problem of reading and writing concurrency, and the most common is to lock the database. In the previous case, we added a lock when we were writing data. All read operations are blocked until the write operation has been completed. This is known as the read – write lock. It’s always going to be slow. Realm takes advantage of the MVCC database and shows up, very fast.

MVCC uses the same source file management algorithm as Git. You can think of the internal Realm as a Git, and it also has branch and atomization operations. This means that you may work on many branches (database versions), but you do not have a full copy of the data. The Realm and the real MVCC database are somewhat different. A true MVCC database like Git, you can have multiple candidates for HEAD on the version tree. While Realm has only one write at a time, and always operates the latest version – it does not work on older versions.

Realm database from entry to

Realm bottom is B+ tree implementation, in the Realm team open source realm-core inside you can see the source code, which useful bpTree, this is a B+ tree implementation. B+ tree is a tree data structure. It is a n fork tree. Each node usually has more than one child. A B+ tree contains root node, internal node and leaf node. The root node may be a leaf node, or it may be a node containing two or more child nodes, two.

B+ trees are commonly used in database and operating system file systems. File systems such as NTFS, ReiserFS, NSS, XFS, JFS, ReFS, and BFS are using the B+ tree as the metadata index. B+ tree is able to keep data stable and orderly, and its insertion and modification have more stable logarithmic time complexity. The B+ tree element is inserted from the bottom up.

Realm database from entry to

Realm allows each connection thread to have snapshots of data at a particular time. That’s why you can do a lot of action in hundreds of threads and access the database at the same time without crashing.

Realm database from entry to

The diagram above shows a good writing process for Realm. There are 3 phases, phase 1 and V1 pointing to the root node R. In phase 2, prepare the write operation, at this time there will be a V2 node, point to the new R’, and create a new branch out of A’ and C’. The corresponding right child points to the right child of R, pointing to the original V1. If the write operation fails, the branch on the left is discarded. Such a design ensures that even if failed, only the latest data is lost without damaging the entire database. If the write is successful, then put the original R, A, and C nodes in the Garbage, and then go to the third stage, written successfully, and turned V2 to the root node.

In the process of writing, the second stage is the most critical, and the write operation does not change the original data, but creates a new branch. This does not require locking, and can also solve the concurrency problem of the database.

It is the design of the underlying data structure + MVCC of the B+ tree that ensures the high performance of Realm.

2.Realm uses the zero-copy architecture

Because Realm uses the zero-copy architecture, there is almost no memory overhead. This is because each Realm object corresponds directly through a local long pointer to the underlying database, which is the hook for data in the database.

The usual traditional database operations are like this. The data is stored in the disk’s database file, and our query requests are translated into a series of SQL statements, creating a database connection. The database server receives the request by the parser for semantic lexical and syntax of the SQL statement analysis, then the SQL statement is optimized by the query optimizer, optimize the execution of corresponding query, read the disk database file (index is read, read from the query index) of each row of data, then saved to memory (here is the memory consumption). After that, you need to sequence the data into a format that can be stored in memory, which means bit alignment so that CPU can handle them. Finally, the data needs to be translated into language level types, and then it will be returned in the form of objects, such as Objective-C objects.

Here is another Realm soon, Realm database files through memory-mapped, that is to say the database file itself is mapped into memory (actually virtual memory) in the Realm to access the file offset like file already in memory (like this in memory refers to the virtual memory), which allows the file in no case do deserialization directly from memory read, improve reading efficiency. Realm simply calculates the offset to find the data in the file, and then returns the value of the data structure from the original access point.

Realm is using the zero-copy structure, almost no memory overhead, Realm core file format based on memory-mapped, cost saving a lot of serialization and deserialization, led Realm to obtain the velocity of the object, especially.

3., Realm objects cannot be shared among different threads

The reason why Realm objects cannot be passed online is to ensure isolation and data consistency. There is only one purpose for this, for speed.

Since Realm is based on zero copy, all objects are in memory, so they are automatically updated. If Realm objects are allowed to share between online programs, Realm will not be able to ensure data consistency because different threads change the object’s data at any point in time.

To make sure that multiple threads share objects, it is a lock, but locks can cause a long background write transaction to block UI read transactions. Without locking, data consistency is not guaranteed, but speed requirements can be met. After measuring Realm, or for speed, a compromise that does not allow threads to share is made.

It is precisely because the object is not allowed to share among different threads, to ensure consistency of data, without thread locks, to ensure that Realm is far ahead of the speed.

4. real lazy loading

Most databases tend to store data at the horizontal level, which is why when you read an attribute from SQLite, you have to load the entire row of data. It is stored continuously in the file.

Unlike Realm, Realm makes it possible to store attributes continuously at vertical levels, and you can also view them as column stores.

After querying a set of data, you really load it only when you actually access the object.

5. files in Realm
Realm database from entry to

Let’s talk about Database File in the middle

The.Realm file is memory mapped, and all objects are a reference to the first address offset of the file. The storage of objects is not necessarily contiguous, but Array is guaranteed to be contiguous storage.

When.Realm performs write operations, there are 3 pointers, one is *current, top, pointer, one is other, top, pointer, and the last is switch bit*.

“Switch bit*” indicates whether top pointer has been used. If used, it represents that the database is already readable.

The the top pointer updates first, followed by the the switch bit update. Because even if the write fails, all data is lost, but this ensures that the database is still readable.

Let’s talk about.Lock file.

The.Lock file will contain the the shared group metadata. This file assumes the responsibility of allowing multiple threads to access the same Realm object.

Finally, talk about Commit logs history

This file will be used to update the index indexes, which will be used for synchronization. Inside the main maintenance of 3 small files, the 2 is data related, and the 1 is the operation of management.

summary

After the above analysis, I deeply felt that Realm was born for speed! In ensuring the ACID requirements, many designs are based on speed. Of course, the core idea of Realm is object driven, which is the core principle of Realm. Realm is essentially an embedded database, but it’s another way of looking at data. It looks at the model and business logic in mobile applications from a different perspective.

Realm is a cross platform, and it’s a good thing that all platforms use the same database. It is believed that more and more developers will be using Realm as a App database.

Reference links

Realm official website
Realm official document
Realm GitHub