Swift-Realm数据库的使用详解

概述

Realm 是一个跨平台的移动数据库引擎,其性能要优于 Core Data 和 FMDB - 移动端数据库性能比较, 我们可以在 Android 端 realm-java,iOS端:Realm-Cocoa,同时支持 OC 和 Swift两种语言开发。其使用简单,免费,性能优异,跨平台的特点广受程序员GG喜爱。

Realm 中文文档

本文将结合一些实战演练讲解 Realm 的用法,干货满满!

Realm 支持如下属性的存储

  • Int,Int8,Int16,Int32 和 Int64
  • Boolean 、 Bool
  • Double 、 Float
  • String
  • NSDate 、 Date(精度到秒)
  • NSData 、 Data
  • 继承自 Object 的类 => 作为一对一关系(Used for One-to-one relations)
  • List => 作为一对多关系(Used for one-to-many relations)
Realm 安装 - 使用 CocoaPods

pod 'RealmSwift'
pod 'Realm'

Realm 配置
  • 在 AppDelegate 的 didFinishLaunchingWithOptions 方法中调用以下方法,这个方法主要用于数据模型属性增加或删除时的数据迁移,每次模型属性变化时,将 dbVersion 加 1 即可,Realm 会自行检测新增和需要移除的属性,然后自动更新硬盘上的数据库架构,移除属性的数据将会被删除。
///配置数据库
   RealmHelper.configRealm()

configRealm方法说明如下

    ///  配置数据库
    public class func configRealm() {
        /// 这个方法主要用于数据模型属性增加或删除时的数据迁移,每次模型属性变化时,将 dbVersion 加 1 即可,Realm 会自行检测新增和需要移除的属性,然后自动更新硬盘上的数据库架构,移除属性的数据将会被删除。
        let dbVersion : UInt64 = 1
        let docPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)[0] as String
        let dbPath = docPath.appending("/defaultDB.realm")
        let config = Realm.Configuration(fileURL: URL.init(string: dbPath), inMemoryIdentifier: nil, syncConfiguration: nil, encryptionKey: nil, readOnly: false, schemaVersion: dbVersion, migrationBlock: { (migration, oldSchemaVersion) in
            
        }, deleteRealmIfMigrationNeeded: false, shouldCompactOnLaunch: nil, objectTypes: nil)
        Realm.Configuration.defaultConfiguration = config
        Realm.asyncOpen { (realm, error) in
            if let _ = realm {
                print("Realm 服务器配置成功!")
            }else if let error = error {
                print("Realm 数据库配置失败:\(error.localizedDescription)")
            }
        }
    }

定义模型

import UIKit
import RealmSwift
import Realm

class Book: Object {
    @objc dynamic var name = ""
    @objc dynamic var author = ""
    
    convenience init(name : String,author : String) {
          self.init();
          self.name = name;
          self.author = author;
      }
    
    /// LinkingObjects 反向表示该对象的拥有者
      let owners = LinkingObjects(fromType: Student.self, property: "books")
    
}

class Student: Object {
  
    @objc dynamic var name = ""
    @objc dynamic var age = 18
    @objc dynamic var weight = 156
    @objc dynamic var id = 0
    @objc dynamic var address = ""
    @objc dynamic var birthday : NSDate? = nil
    @objc dynamic var photo : NSData?  = nil
    
    //重写 Object.primaryKey() 可以设置模型的主键。
      //声明主键之后,对象将被允许查询,更新速度更加高效,并且要求每个对象保持唯一性。
      //一旦带有主键的对象被添加到 Realm 之后,该对象的主键将不可修改。
    override static func primaryKey() -> String? {
          return "id"
      }

      //重写 Object.ignoredProperties() 可以防止 Realm 存储数据模型的某个属性
      override static func ignoredProperties() -> [String] {
          return ["tempID"]
      }

      //重写 Object.indexedProperties() 方法可以为数据模型中需要添加索引的属性建立索引,Realm 支持为字符串、整型、布尔值以及 Date 属性建立索引。
      override static func indexedProperties() -> [String] {
          return ["name"]
      }
      
      //List 用来表示一对多的关系:一个 Student 中拥有多个 Book。
      let books = List<Book>()

}

需要注意的是:在使用Realm中存储的数据模型都要是 Object 类的子类。
  1. 设置主键 - primaryKey

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

 override static func primaryKey() -> String? {
        return "id"
    }
  1. 忽略属性 - ignoredProperties

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

    override static func ignoredProperties() -> [String] {
        return ["tempID"]
    }

3)索引属性 - indexedProperties

重写 Object.indexedProperties() 方法可以为数据模型中需要添加索引的属性建立索引,Realm 支持为字符串、整型、布尔值以及 Date 属性建立索引。

    override static func indexedProperties() -> [String] {
        return ["name"]
    }

4)使用List实现一对多关系 - indexedProperties

List 用来表示一对多的关系:一个 Student 中拥有多个 Book。
List 中可以包含简单类型的 Object,表面上和可变的 Array 非常类似,所用的方法和访问数据的方式(索引和下标)都相同,并且所包含的所有对象都应该是相同类型的。声明前面不可加 dynamic ,因为在 Objective-C 运行时无法表示泛型属性。
注意:List 只能够包含 Object 类型,不能包含诸如String之类的基础类型。

    //List 用来表示一对多的关系:一个 Student 中拥有多个 Book。
    let books = List<Book>()

5)反向关系 - LinkingObjects

通过反向关系(也被称为反向链接(backlink)),您可以通过一个特定的属性获取和给定对象有关系的所有对象。 Realm 提供了“链接对象 (linking objects)” 属性来表示这些反向关系。借助链接对象属性,您可以通过指定的属性来获取所有链接到指定对象的对象。
例如:一个 Book 对象可以拥有一个名为 owners 的链接对象属性,这个属性中包含了某些 Student 对象,而这些 Student 对象在其 books 属性中包含了这一个确定的 Book 对象。您可以将 owners 属性设置为 LinkingObjects 类型,然后指定其关系,说明其当中包含了其拥有者 Student 对象。

  let owners = LinkingObjects(fromType: Student.self, property: "books")

1.增

    ///新增单条数据
    public class func addObject<T>(object: T){
        
        do {
            let defaultRealm = self.getDB()
            try defaultRealm.write {
                defaultRealm.add(object as! Object)
            }
            print(defaultRealm.configuration.fileURL ?? "")
        } catch {}
        
    }
        
    /// 保存多条数据
    public class func addObjects<T>(by objects : [T]) -> Void {
        let defaultRealm = self.getDB()
        try! defaultRealm.write {
            defaultRealm.add(objects as! [Object])
        }
        print(defaultRealm.configuration.fileURL ?? "")
    }

2.删

    /// 删除单条
    /// - Parameter object: 删除数据对象
    public class func deleteObject<T>(object: T?) {
        
        if object == nil {
            print("无此数据")
            return
        }
        
        do {
              let defaultRealm = self.getDB()
              try defaultRealm.write {
                  defaultRealm.delete(object as! Object)
              }
        } catch {}
    }
    
    
    /// 删除多条数据
    /// - Parameter objects: 对象数组
    public class func deleteObjects<T>(objects: [T]?) {
        
        if objects?.count == 0 {
            print("无此数据")
            return
        }
        
        do {
              let defaultRealm = self.getDB()
              try defaultRealm.write {
                  defaultRealm.delete(objects as! [Object])
              }
        } catch {}
    }

    
    /// 根据条件去删除单条/多条数据
    public class func deleteObjectFilter<T>(objectClass: T, filter: String?) {
        
        let objects = RealmHelper.queryObject(objectClass: objectClass, filter: filter)
        RealmHelper.deleteObjects(objects: objects)
        
    }
    
   
    /// 删除某张表
    /// - Parameter objectClass: 删除对象
      public class func clearTableClass<T>(objectClass: T) {
          
          do {
                let defaultRealm = self.getDB()
                try defaultRealm.write {
                    defaultRealm.delete(defaultRealm.objects((T.self as! Object.Type).self))
                }
          } catch {}
      }

3.更新

///更新单条数据
    public class func updateObject<T>(object: T) {
        
        do {
            let defaultRealm = self.getDB()
            try defaultRealm.write {
                defaultRealm.add(object as! Object, update: .error)
            }
        }catch{}
    }
    
    
    
    /// 更新多条数据
    public class func updateObjects<T>(objects : [T]) {
        let defaultRealm = self.getDB()
        try! defaultRealm.write {
            defaultRealm.add(objects as! [Object], update: .error)
        }
    }
    
    /// 更新多条数据的某一个属性
    public class func updateObjectsAttribute<T>(objectClass : T ,attribute:[String:Any]) {
        let defaultRealm = self.getDB()
        try! defaultRealm.write {
            let objects = defaultRealm.objects((T.self as! Object.Type).self)
            let keys = attribute.keys
             for keyString in keys {
                objects.setValue(attribute[keyString], forKey: keyString)
            }
        }
    }

4.查询

/// 查询数据
    /// - Parameters:
    ///   - objectClass: 当前查询对象
    ///   - filter: 查询条件
    class func queryObject <T> (objectClass: T, filter: String? = nil) -> [T]{
        
        let defaultRealm = self.getDB()
        var results : Results<Object>
        
        if filter != nil {
                 
            results =  defaultRealm.objects((T.self as! Object.Type).self).filter(filter!)
        }
        else {
                 
            results = defaultRealm.objects((T.self as! Object.Type).self)
        }
        
        guard results.count > 0 else { return [] }
        var objectArray = [T]()
        for model in results{
           objectArray.append(model as! T)
        }
       
        return objectArray
        
    }

注:

通过 Realm Browser 查看刚才保存的数据

通过 print(realm.configuration.fileURL ?? "") 打印数据路径

以下提供Demo地址

Demo下载地址

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,511评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,495评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,595评论 0 225
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,558评论 0 190
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,715评论 3 270
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,672评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,112评论 2 291
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,837评论 0 181
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,417评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,928评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,316评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,773评论 2 234
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,253评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,827评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,440评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,523评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,583评论 2 249

推荐阅读更多精彩内容