美文网首页
realm 数据库实践

realm 数据库实践

作者: samtake | 来源:发表于2019-08-22 16:30 被阅读0次
image.png

realm官网传送门
Object-C版本具体接口
个人推荐这篇Realm数据库 从入门到“放弃”

看完请记住这两点:

  • 非线程安全的,读写必须和realm对象在同一进程。跨线程访问数据库,Realm对象一定需要新建一个。
  • transactionWithBlock 已经处于一个写的事务中,事务之间不能嵌套
RLMRealm *realm = [RLMRealm defaultRealm];
    [realm transactionWithBlock:^{
        //block里面不能再有事务操作了,否则会报错
        [Person createInRealm:realm withValue:@[@"John", @[@[@"Fido", @1]]]];
        [Person createInRealm:realm withValue:@[@"Mary", @[@[@"Rex", @2]]]];
    }];

OK,看到这里你可以放弃了

皮一下也挺开心的。哈哈哈
参考官方demo,开始动手撸代码吧!

realm的基本使用

模型

RLM_ARRAY_TYPE(Dog) //RLM_ARRAY_TYPE宏创建了一个协议,从而允许RLMArray<Dog> *dogs语法的使用
@interface Person : RLMObject
@property NSString      *name;
@property RLMArray <Dog> *dogs;
@end

操作

#pragma mark - 创建数据库
-(void)creatData{
    NSLog(@"创建数据库");
    [[NSFileManager defaultManager] removeItemAtURL:[RLMRealmConfiguration defaultConfiguration].fileURL error:nil];
}
#pragma mark - 创建模型对象、并赋值
-(void)creatModelObject{
    // Create a standalone object
   mydog  = [[Dog alloc] init];
    
    // Set & read properties
    mydog.name = @"创建模型对象、并赋值 Rex";
    mydog.age = 9;
    NSLog(@"创建模型对象、并赋值 mydog=%@",mydog);
}


#pragma mark - 保存数据
-(void)addData{
    if (!mydog) {
        NSLog(@"请创建模型对象、并赋值");
        return;
    }
    // Realms are used to group data together
    RLMRealm *realm = [RLMRealm defaultRealm]; // Create realm pointing to default file
    
    // Save your object
    [realm beginWriteTransaction];
    [realm addObject:mydog];
    [realm commitWriteTransaction];
    NSLog(@"保存数据");
}

#pragma mark - 查询数据
-(void)queryData{
    NSLog(@"查询数据");
    // Query
    RLMRealm *realm = [RLMRealm defaultRealm];
    RLMResults *results = [Dog objectsInRealm:realm where:@"name contains 'x'"];
    
    // Queries are chainable!
    RLMResults *results2 = [results objectsWhere:@"age > 8"];
    NSLog(@"Number of dogs: %li", (unsigned long)results2.count);
    
    // Link objects
    Person *person = [[Person alloc] init];
    person.name = @"Tim";
    [person.dogs addObject:mydog];
    
    [realm beginWriteTransaction];
    [realm addObject:person];
    [realm commitWriteTransaction];
    
    // Multi-threading
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        @autoreleasepool {
            RLMRealm *otherRealm = [RLMRealm defaultRealm];
            RLMResults *otherResults = [Dog objectsInRealm:otherRealm where:@"name contains 'Rex'"];
            NSLog(@"Number of dogs: %li", (unsigned long)otherResults.count);
        }
    });
}

输出

2019-08-22 11:05:38.228017+0800 rmMigrations-OC[7537:133107] 创建数据库
2019-08-22 11:05:39.677837+0800 rmMigrations-OC[7537:133107] 创建模型对象、并赋值 mydog=Dog {
    name = 创建模型对象、并赋值 Rex;
    age = 9;
}
2019-08-22 11:05:41.362775+0800 rmMigrations-OC[7537:133107] 保存数据
2019-08-22 11:05:42.413749+0800 rmMigrations-OC[7537:133107] 查询数据
2019-08-22 11:05:42.426567+0800 rmMigrations-OC[7537:133107] Number of dogs: 1
2019-08-22 11:05:42.430851+0800 rmMigrations-OC[7537:135529] Number of dogs: 1
realm的基本使用.png

做一下修改,创建数据库的配置写在AppDelegate

#pragma mark - 创建数据库的配置
- (void)creatrRealmDataBaseWithName:(NSString *)databaseName
{
    NSArray *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *path = [docPath objectAtIndex:0];
    NSString *filePath = [path stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.realm",databaseName]];//添加.realm便于识别
    NSLog(@"realm数据库目录 = %@",filePath);
    
    RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
    config.fileURL = [NSURL URLWithString:filePath];
    //config.objectClasses = @[MyClass.class, MyOtherClass.class];//限制某些模型进行数据迁移
    config.readOnly = NO;
    int currentVersion = 1.0;
    config.schemaVersion = currentVersion;
    
    config.migrationBlock = ^(RLMMigration *migration , uint64_t oldSchemaVersion) {
        // 这里是设置数据迁移的block
        if (oldSchemaVersion < currentVersion) {
            NSLog(@"migrations");
        }
    };
    
    [RLMRealmConfiguration setDefaultConfiguration:config];
}

打开数据库所在目录,然而没有发现有relam数据库,只有 wegood.note的标记

➜  Documents cd /Users/samtake/Library/Developer/CoreSimulator/Devices/5F2978C1-3DEF-4BFF-92B3-9955E7A4AA53/data/Containers/Data/Application/9008C4E0-827B-4ED5-9040-B026739FB23F/Documents/
➜  Documents ls
default.realm.management wegood.management
default.realm.note       wegood.note
➜  Documents open .
➜  Documents 

这时需要存入数据才会生成wegood.realm


image.png

反向链接

加密

model字段的自加操作

数据更新通知

关于通知先看下源码里面对涉及的函数说明

/**
 Registers a block to be called each time the results collection changes.

 The block will be asynchronously called with the initial results collection,
 and then called again after each write transaction which changes either any
 of the objects in the results, or which objects are in the results.

 The `change` parameter will be `nil` the first time the block is called.
 For each call after that, it will contain information about
 which rows in the results collection were added, removed or modified. If a
 write transaction did not modify any objects in the results collection,
 the block is not called at all. See the `RLMCollectionChange` documentation for
 information on how the changes are reported and an example of updating a
 `UITableView`.

 If an error occurs the block will be called with `nil` for the results
 parameter and a non-`nil` error. Currently the only errors that can occur are
 when opening the Realm on the background worker thread.

 At the time when the block is called, the `RLMResults` object will be fully
 evaluated and up-to-date, and as long as you do not perform a write transaction
 on the same thread or explicitly call `-[RLMRealm refresh]`, accessing it will
 never perform blocking work.

 Notifications are delivered via the standard run loop, and so can't be
 delivered while the run loop is blocked by other activity. When
 notifications can't be delivered instantly, multiple notifications may be
 coalesced into a single notification. This can include the notification
 with the initial results. For example, the following code performs a write
 transaction immediately after adding the notification block, so there is no
 opportunity for the initial notification to be delivered first. As a
 result, the initial notification will reflect the state of the Realm after
 the write transaction.

     RLMResults<Dog *> *results = [Dog allObjects];
     NSLog(@"dogs.count: %zu", dogs.count); // => 0
     self.token = [results addNotificationBlock:^(RLMResults *dogs,
                                                  RLMCollectionChange *changes,
                                                  NSError *error) {
         // Only fired once for the example
         NSLog(@"dogs.count: %zu", dogs.count); // => 1
     }];
     [realm transactionWithBlock:^{
         Dog *dog = [[Dog alloc] init];
         dog.name = @"Rex";
         [realm addObject:dog];
     }];
     // end of run loop execution context

 You must retain the returned token for as long as you want updates to continue
 to be sent to the block. To stop receiving updates, call `-invalidate` on the token.

 @warning This method cannot be called during a write transaction, or when the
          containing Realm is read-only.

 @param block The block to be called whenever a change occurs.
 @return A token which must be held for as long as you want updates to be delivered.
 */
- (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMResults<RLMObjectType> *__nullable results,
                                                         RLMCollectionChange *__nullable change,
                                                         NSError *__nullable error))block __attribute__((warn_unused_result));

addNotificationBlock函数写在RLMResults类里的,

  • 注意⚠️一下:在写入事务期间,或者当包含realm时是只读的(这个我的理解是嵌套realm,但这样子不是会崩溃吗??)
  • 参数是:每次发生更改时都会调用里面的block
  • 返回一个RLMNotificationToken类型的token,只要您希望传递更新,它就必须保留。所以一般都是通过strong来声明。
self.array = [[TableViewModel allObjects]sortedResultsUsingKeyPath:@"name" ascending:YES];
/**
 Whether the descriptor sorts in ascending or descending order.
 */
@property (nonatomic, readonly) BOOL ascending;

根据描述符是按升序还是降序排序

  • YES 升序
  • NO 降序

数据迁移升级

数据迁移官方demo一共列出了出了三种类型

  • 合并字段的迁移
  • 增加新字段的迁移
  • 原字段重命名的迁移
 RLMMigrationBlock migrationBlock = ^(RLMMigration *migration, uint64_t oldSchemaVersion) {
        if (oldSchemaVersion < 1) {//合并字段的迁移
            [migration enumerateObjects:Person.className block:^(RLMObject *oldObject, RLMObject *newObject) {
                if (oldSchemaVersion < 1) {
                    // combine name fields into a single field
                    newObject[@"fullName"] = [NSString stringWithFormat:@"%@ %@", oldObject[@"firstName"], oldObject[@"lastName"]];
                }
            }];
        }
        if (oldSchemaVersion < 2) {//增加新字段的迁移
            NSLog(@"[RLMRealmConfiguration defaultConfiguration].fileURL=%@",[RLMRealmConfiguration defaultConfiguration].fileURL);
            [migration enumerateObjects:Person.className block:^(RLMObject *oldObject, RLMObject *newObject) {
                // give JP a dog
                if ([newObject[@"fullName"] isEqualToString:@"JP McDonald"]) {
                    Pet *jpsDog = [[Pet alloc] initWithValue:@[@"Jimbo", @(AnimalTypeDog)]];
                    [newObject[@"pets"] addObject:jpsDog];
                }
            }];
        }
        if (oldSchemaVersion < 3) {//原字段重命名的迁移
            NSLog(@"[RLMRealmConfiguration defaultConfiguration].fileURL=%@",[RLMRealmConfiguration defaultConfiguration].fileURL);
            [migration enumerateObjects:Pet.className block:^(RLMObject *oldObject, RLMObject *newObject) {
                // convert type string to type enum if we have outdated Pet object
                if (oldObject && oldObject.objectSchema[@"type"].type == RLMPropertyTypeString) {
                    newObject[@"type"] = @([Pet animalTypeForString:oldObject[@"type"]]);
                }
            }];
        }
        NSLog(@"Migration complete.");
    };

增加新字段的迁移实操

v0版本

//v0
@property NSString *name;
@property NSInteger age;
//v0
- (void)creatrRealmDataBase{
    NSArray *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *path = [docPath objectAtIndex:0];
    NSString *filePath = [path stringByAppendingPathComponent:@"wegood_v0.realm"];//添加.realm便于识别
    NSLog(@"realm数据库目录 = %@",filePath);

    RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
    config.fileURL = [NSURL URLWithString:filePath];
    NSLog(@"schemaVersion=%llu",config.schemaVersion);
    [RLMRealmConfiguration setDefaultConfiguration:config];
}
image.png

v1版本

//v1
@property NSString *oneWorld;
@property NSString *name;
@property NSInteger age;
//v1
- (void)creatrRealmDataBase{
    NSArray *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *path = [docPath objectAtIndex:0];
    
    //旧数据库地址
    NSString *filePathOld = [path stringByAppendingPathComponent:@"wegood_v0.realm"];//添加.realm便于识别
    NSLog(@"filePathOld = %@",filePathOld);
    NSURL *oldURL = [NSURL URLWithString:filePathOld];
    
    
    //新数据库地址
    NSString *filePathNew = [path stringByAppendingPathComponent:@"wegood_v1.realm"];
    NSLog(@"filePathNew = %@",filePathNew);
    NSURL *newURL = [NSURL URLWithString:filePathNew];
    
    [[NSFileManager defaultManager] removeItemAtURL:oldURL error:nil];
    [[NSFileManager defaultManager] copyItemAtURL:oldURL toURL:newURL error:nil];
    
    RLMMigrationBlock migrationBlock = ^(RLMMigration *migration , uint64_t oldSchemaVersion) {
        // 这里是设置数据迁移的block
        if (oldSchemaVersion == 1) {
            /*if(currentVersion==1.0){*/
            NSLog(@"migrations");
            [migration enumerateObjects:Dog.className block:^(RLMObject *oldObject, RLMObject *newObject) {
                [newObject[@"oneWorld"] addObject:@"数据迁移的默认值"];
            }];
        }
    };
    
    
    RLMRealmConfiguration *configuration = [RLMRealmConfiguration defaultConfiguration];
    configuration.schemaVersion = 1;
    configuration.migrationBlock = migrationBlock;
    [RLMRealmConfiguration setDefaultConfiguration:configuration];
    [RLMRealm defaultRealm];
    
    
    // set schemave versions and migration blocks form Realms at each path
    RLMRealmConfiguration *realmv1Configuration = [configuration copy];
    realmv1Configuration.fileURL = newURL;
    
    
    // manully migration v1Path, v2Path is migrated implicitly on access
    [RLMRealm performMigrationForConfiguration:realmv1Configuration error:nil];
}
image.png

demo链接https://github.com/samtake/rmMigrations-OC
后续再更...

相关文章

网友评论

      本文标题:realm 数据库实践

      本文链接:https://www.haomeiwen.com/subject/fylisctx.html