iOS 开发收藏问题iOS开发

【iOS 开发】Realm Swift 数据库使用汇总

2017-07-21  本文已影响2631人  爱吃鸭梨的猫
Realm

由于最近公司需要将项目用 Swift 改写,项目中需要大量使用数据库,之前 OC 使用的是 Core DataCore Data 使用起来确实十分的繁琐,故决定在 Swift 中弃用,改用 Realm 数据库,下面将使用方法记录下来方便以后查看。


Realm 的优点

Realm 不是基于 Core Data,也不是基于 SQLite 封装构建的。它有自己的数据库存储引擎,下面说一下 Realm 的一些优点。

Realm Browser

Realm Swift 的安装

这是 RealmGitHub 地址 ,其他方法我就不说了,我是用 CocoaPods 方式安装的,所以就只说 CocoaPods 的安装方法了。

post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      config.build_settings['SWIFT_VERSION'] = '3.0'
    end
  end
end

Realm Browser 的使用

先说一下 Realm Browser 这个数据库查看工具的使用方法。

1. 模拟器调试

let realm = try! Realm()
print(realm.configuration.fileURL!)
.realm 文件

2. 真机调试

Devices Download Container

Realm Swift 的使用

1. 配置 Realm 数据库

/* Realm 数据库配置,用于数据库的迭代更新 */
let schemaVersion: UInt64 = 0
let config = Realm.Configuration(schemaVersion: schemaVersion, migrationBlock: { migration, oldSchemaVersion in
    
    /* 什么都不要做!Realm 会自行检测新增和需要移除的属性,然后自动更新硬盘上的数据库架构 */
    if (oldSchemaVersion < schemaVersion) {}
})
Realm.Configuration.defaultConfiguration = config
Realm.asyncOpen { (realm, error) in
    
    /* Realm 成功打开,迁移已在后台线程中完成 */
    if let _ = realm {
        
        print("Realm 数据库配置成功")
    }
    /* 处理打开 Realm 时所发生的错误 */
    else if let error = error {
        
        print("Realm 数据库配置失败:\(error.localizedDescription)")
    }
}
let config = Realm.Configuration(schemaVersion: schemaVersion, migrationBlock: { migration, oldSchemaVersion in
    
    if (oldSchemaVersion < schemaVersion) {
    
        migration.enumerateObjects(ofType: Dog.className()) { oldObject, newObject in
            
            /* 将 Dog 表中旧的 firstName 和 lastName 属性删除,数据保留合并为 fullName 属性 */
            let firstName = oldObject!["firstName"] as! String
            let lastName = oldObject!["lastName"] as! String
            newObject!["fullName"] = "\(firstName) \(lastName)"
        }
    }
})
let config = Realm.Configuration(schemaVersion: schemaVersion, migrationBlock: { migration, oldSchemaVersion in
    
    if (oldSchemaVersion < schemaVersion) {
    
        /* 将 Dog 表的 name 属性重命名为 fullName */
        migration.renameProperty(onType: Dog.className(), from: "name", to: "fullName")
    }
})

2. Model 数据模型

Realm 数据模型是基于标准 Swift 类来进行定义的,使用属性来完成模型的具体定义,Realm 模型对象在形式上基本上与其他 Swift 对象相同,你可以给它们添加您自己的方法和协议,和在其他对象中使用类似。

Realm 支持的属性类

Realm 支持这几种属性类型:BoolInt8Int16Int32Int64DoubleFloatStringNSDate 以及 NSData ,下面的表格提供了关于声明模型属性的简易参考。

类型 非可选值形式 可选值形式
Bool dynamic var value = false let value = RealmOptional<Bool>()
Int dynamic var value = 0 let value = RealmOptional<Int>()
Float dynamic var value: Float = 0.0 let value = RealmOptional<Float>()
Double dynamic var value: Double = 0.0 let value = RealmOptional<Double>()
String dynamic var value = "" dynamic var value: String? = nil
Data dynamic var value = NSData() dynamic var value: NSData? = nil
Date dynamic var value = NSDate() dynamic var value: NSDate? = nil
Object 必须是可选值 dynamic var value: Class?
List let value = List<Class>() 必须是非可选值
LinkingObjects let value = LinkingObjects(fromType: Class.self, property: "property") 必须是非可选值

Model 数据模型创建

下面以 DogPerson 为例,通过简单的继承 Object 或者一个已经存在的模型类,你就可以创建一个新的 Realm 数据模型对象。

普通的数据模型
/// 狗狗的数据模型
class Dog: Object {

    dynamic var name: String?
    dynamic var age = 0
}

/// 狗狗主人的数据模型
class Person: Object {

    dynamic var name: String?
    dynamic var birthdate = NSDate()
}
关系绑定
/// 狗狗的数据模型
class Dog: Object {

    dynamic var name: String?
    dynamic var age = 0
    dynamic var owner: Person?  // 对一关系
}

/// 狗狗主人的数据模型
class Person: Object {

    dynamic var name: String?
    dynamic var birthdate = NSDate()
    let dogs = List<Dog>()  // 对多关系
}
反向关系

如果对多关系属性 Person.dogs 链接了一个 Dog 实例,而这个实例的对一关系属性 Dog.owner 又链接到了对应的这个 Person 实例,那么实际上这些链接仍然是互相独立的。

Person 实例的 dogs 属性添加一个新的 Dog 实例,并不会将这个 Dog 实例的 owner 属性自动设置为该 Person

但是由于手动同步双向关系会很容易出错,并且这个操作还非常得复杂、冗余,因此 Realm 提供了 链接对象 (linking objects) 属性来表示这些反向关系。

/// 狗狗的数据模型
class Dog: Object {

    dynamic var name: String?
    dynamic var age = 0
    let owner = LinkingObjects(fromType: Person.self, property: "dogs") // 反向关系
}

/// 狗狗主人的数据模型
class Person: Object {

    dynamic var name: String?
    dynamic var birthdate = NSDate()
    let dogs = List<Dog>()  // 对多关系
}
索引属性(Indexed Properties)

重写 Object.indexedProperties() 方法可以为数据模型中需要添加索引的属性建立索引。Realm 支持字符串整数布尔值 以及 NSDate 属性作为索引。对属性进行索引可以减少插入操作的性能耗费,加快比较检索的速度(比如说 = 以及 IN 操作符)

/// 狗狗的数据模型
class Dog: Object {

    dynamic var name: String?
    dynamic var age = 0

    override class func indexedProperties() -> [String] {

        return ["name"]
    }
}
主键(Primary Keys)

重写 Object.primaryKey() 可以设置模型的主键。声明主键之后,对象将允许进行查询,并且更新速度更加高效,而这也会要求每个对象保持唯一性。 一旦带有主键的对象被添加到 Realm 之后,该对象的主键将不可修改。

Realm 可以将 IntString 类型的属性设为主键,但是不支持自增长属性,所以只能自己给主键生成一个唯一的标识,可以使用 UUID().uuidString 方法生成唯一主键。

/// 狗狗的数据模型
class Dog: Object {
    
    dynamic var id = UUID().uuidString
    dynamic var name: String?
    dynamic var age = 0
    
    override class func primaryKey() -> String? {

        return "id" 
    }
}
忽略属性(Ignored Properties)

重写 Object.ignoredProperties() 可以防止 Realm 存储数据模型的某个属性。Realm 将不会干涉这些属性的常规操作,它们将由成员变量提供支持,并且您能够轻易重写它们的 settergetter

/// 狗狗的数据模型
class Dog: Object {
    
    dynamic var name: String?
    dynamic var age = 0
    
    override class func ignoredProperties() -> [String]? {

        return ["name"] 
    }
}

3. 创建数据模型对象

/* (1) 创建一个狗狗对象,然后设置其属性 */
var myDog = Dog()
myDog.name = "大黄"
myDog.age = 10

/* (2) 通过字典创建狗狗对象 */
let myOtherDog = Dog(value: ["name" : "豆豆", "age": 3])

/* (3) 通过数组创建狗狗对象 */
let myThirdDog = Dog(value: ["豆豆", 5])
/* 这里我们就可以使用已存在的狗狗对象来完成初始化 */
let aPerson = Person(value: ["李四", 30, [aDog, anotherDog]])

/* 还可以使用多重嵌套 */
let anotherPerson = Person(value: ["李四", 30, [["小黑", 5], ["旺财", 6]]])

4. 数据库操作(增删改查)

任何操作都需要获取 Realm 实例,每个线程只需要使用一次即可。

/* 获取默认的 Realm 实例,每个线程只需要使用一次即可 */
let realm = try! Realm()

增加数据

/* 创建一个 Dog 对象 */
let dog = Dog(value: ["name" : "豆豆", "age": 3])

/* 创建一个 Dog 对象数组 */
let dogs = [Dog(value: ["name": "张三", "age": 1]), Dog(value: ["name": "李四", "age": 2]), Dog(value: ["name": "王五", "age": 3])]

/* 通过事务将数据添加到 Realm 中 */
try! realm.write {

    realm.add(dog) // 增加单个数据
    realm.add(dogs) // 增加多个数据
    realm.create(Dog.self, value: ["name" : "豆豆", "age": 3], update: true) // 直接根据 JSON 数据增加
}

删除数据

// let dog = ... 存储在 Realm 中的 Dog 对象
// let dogs = ... 存储在 Realm 中的多个 Dog 对象

/* 在事务中删除数据 */
try! realm.write {

    realm.delete(dog) // 删除单个数据
    realm.delete(dogs) // 删除多个数据
    realm.deleteAll() // 从 Realm 中删除所有数据
}

修改数据

// let dog = ... 存储在 Realm 中的 Dog 对象

/* 在一个事务中修改数据 */
try! realm.write {

    dog.name = "张三"
}
// let dog = ... 存储在 Realm 中的 Dog 对象(有主键)
// let dogs = ... 存储在 Realm 中的多个 Dog 对象(有主键)

/* 在一个事务中修改数据 */
try! realm.write {

    realm.add(dog, update: true) // 更新单个数据
    realm.add(dogs, update: true) // 更新多个数据
}
// let dogs = ... 存储在 Realm 中的多个 Dog 对象

/* 在一个事务中修改数据 */
try! realm.write {

    dogs.first?.setValue("张三", forKeyPath: "name") // 将第一个狗狗名字改为张三
    dogs.setValue("张三", forKeyPath: "name") // 将所有狗狗名字都改为张三
}

查询数据

/* 从数据库中查询所有狗狗 */
let dogs = realm.objects(Dog.self)
/* 从数据库中查询主键为 1 的狗狗 */
let dog = realm.object(ofType: Dog.self, forPrimaryKey: "1")
/* 根据断言字符串从数据库查询 name 为 张三 的狗狗 */
var dogs = realm.objects(Dog.self).filter("name = %@", "张三")

/* 根据 NSPredicate 谓词从数据库查询 age 小于 5 并且 name 以 ‘张’ 开头的狗狗 */
let predicate = NSPredicate(format: "age < 5 AND name BEGINSWITH '张'")
var dogs = realm.objects(Dog.self).filter(predicate)
/* 将查询到的狗狗根据名字升序进行排序 */
let dogs = realm.objects(Dog.self).sorted(byKeyPath: "name")

/* 将查询到的狗狗根据名字降序进行排序 */
let dogs = realm.objects(Dog.self).sorted(byKeyPath: "name", ascending: false)

/* 将查询到的狗狗根据名字和年龄升序进行排序 */
let dogs = realm.objects(Dog.self).sorted(by: ["name", "age"])

想要了解更多可以查看 中文官方文档地址 ,有不足之处之后会补充,OC 版本的话可以看这篇文章:Realm数据库 从入门到“放弃” ,写的非常详细,也参考了不少这篇文章的内容。

将来的你,一定会感激现在拼命的自己,愿自己与读者的开发之路无限美好。

我的传送门: 博客简书微博GitHub

上一篇 下一篇

猜你喜欢

热点阅读