Realm.Object与NSObject的转换中swift协议妙用的体现
本文字数:3380字
预计阅读时间:10分钟
概述
简单介绍一下,Realm数据库:
Realm 是一个跨平台的移动数据库引擎,于 2014 年 7 月发布,准确来说,它是专门为移动应用所设计的数据持久化解决方案之一。优点主体现在:1.跨平台 2.简单易用,并且相对于iOS中使用的SQLite 以及 Core Data 要快很多 3.可视化
在项目中由于使用到了 Realm数据库,就避免不了创建Realm数据库特有的数据库模型:基本类型为
Realm.Object的模型。之后下文都将使用
Realm.Object代表realm数据模型。
由于
Realm.Object的特殊性,它不可跨线程使用,特有的list等基础类型,以及为了与业务关系解耦等原因,业务中不直接使用
Realm.Object。所以我们给每个
Realm.Object模型,创建了一个对应的业务模型下文简称为
模型
如图1,我们需要在两种对应模型之间进行转换。为了减少重复的代码,就需要写一个模型转换功能。
想到要写一个模型转换功能?我们应该能想到有很多
Dictionary或者
Array与
模型互转的框架,这些是开发中很常见的功能。这里不在赘述他们的原理,毕竟OC,swift中的转换思路和现有库,想必各位读者应该都看过或者写过不少。
简单描述一下已有框架的使用流程,如果从A类
模型转换成B类
模型。利用这些框架,我们想到的应该是 A类
模型先转换成
Dictionary或者json,然后
Dictionary或者json转换成B类
模型,使用已有框架基本都是这种套路。
本文章要阐述的不是如何使用这些框架,也不是单纯讲转换原理,而是采用更加swift的方式来实现转换功能。至于为什么要"多此一举"自己写转换具体有如下原因。
swift转换框架大部分都是使用的OC原有Api思路来转换,换汤不换药,虽然还是有一个比较特殊的直接获取指针赋值的,但是在当时(大概两年前)又不够稳定
我们有
Realm.Object
这种类型的模型,它有自定义的基本类型,目前已有框架是没法直接使用来进行转换的。不过他提供了Dictionary
生成Realm.Object
的方法(这个方法还有待改进),可惜他没有提供Realm.Object
生成Dictionary
的方法上面我提过了已有套路实现转换要分两步,能不能有方式可以直接
Realm.Object
与模型
互转?实现转换的方式过程中少用或者尽量不用OC的原有运行时,符号类型判断,对象动态创建等,尽量使用swift官方标配protocol来做这件事
上面的原因条件都说了,那就开始准备咯。既然自己造轮子,那当然得造出适合自己的轮子。在这转换的实现过程中swift协议起到了不可或缺的作用,希望现在分享出来也不算太晚。正文开始咯~
转换之前的准备
概述中已经介绍了写这转换功能的初衷,我们要做的
Realm.Object与普通业务
模型转换。在此先列下写这转换时的想法:
你要对
Realm.Object
有足够的了解,充分利用Realm.Object
的已有特性转换方式尽量使用swift特性
能不能直接由普通类型
模型
转换成Realm.Object
,减少中间的Dictionary
步骤?
要实现这个转换主要涉及到的具体问题
swift 类或结构体
Any.Type
创建对象(包括NSObject
,Option,Array,Dictionary
等泛型类型)Realm
List<T>
类型与[T]
类型通用转换RealmObject类型属性遍历(需要翻看realm源码,因为官方文档其实也没有提及,我也不带大家看源码了,有兴趣可以私我分享realm,哈哈哈)以及NSObject类型属性遍历方式
从基础代码开始
首先为了功能,我们定义协议,并写好转换方法名称。这样业务
模型(需继承NSObject,实际赋值那步需要使用KVC)遵循
Persistable协议后,就可以使用了
protocol Persistable { associatedtype ManagedObject: RealmSwift.Object //业务模型转RealmObject方法 public func managedObject() -> ManagedObject //RealmObject转业务模型方法 public static func modelInstance(managedObject:RealmSwift.Object) -> Self }
Realm.Object
转换成 普通业务模型
接下来我们先完成public static func modelInstance(managedObject:RealmSwift.Object) -> Self的默认实现。
实现如下
extension Persistable where Self:NSObject { public static func modelInstance(managedObject:RealmSwift.Object) -> Self { var instance:Self instance = Self._creatInstance() AutoExchangeHelper.updateModelWithObject(model: instance , managedObject: managedObject) return instance } }以上协议实现分两步:
创建业务模型
遍历业务模型属性进行赋值
第一步如何创建对象?想一想,在协议类中我们不知道该具体类型,该如何创建对象?在该协议的扩展中,我们知道遵循该协议的是
NSObject类型,而只要是继承
NSObject类型其实都可以使用
init方法创建对象。比较简单的方案如下,给NSObject类型添加扩展。可能有点难想到
extension NSObject { static func _creatInstance() -> Self { return self.init() } }第二步 遍历普通业务
模型。这个大家应该比较熟悉,runtime相关api,想必大家已了然于胸。在swift我们可以使用
Mirror来遍历model属性,得到属性的类型
Any.Type,然后判断类型继续操作。我们看下面代码
extension AutoExchangeHelper { static func updateModelWithObject<T:NSObject>(model:T,managedObject:Object) { Mirror(reflecting: model).children.forEach { (child) in guard let name = child.label else{ return } //realm的scheme 包含属性的所有信息,可以用来跟我们的Mirror遍历的类型进行对比容错。 let scheme = managedObject.objectSchema let propertype = scheme[name].type // Realm.Object对象对应属性类型 let modelType:Any.Type = type(of: child.value)//业务模型对象属性对应类型 let objectValue = managedObject[name] //数据库模型对应属性值 if propertype == RLMPropertyType.object { //包含对象 以及数组(数组内容非基础数据类型) if 对象 { ... }else if 集合 { ... } }else { //包含 String ,Int ,List<String> List<Int> 等 } } } }上述代码得到了
modelType:Any.Type类型,接下来的思路如下。已知的有
model(业务模型)
判断属性类型的具体类型
如果是基础类型直接使用
setValue:forKey
赋值,如果是非基础类型,我们需要创建对应类型的对象(递归给其属性赋值),然后使用
setValue:forKey
赋值
属性是基础类型的情况
realm的
RLMPropertyType判断出的基础类型 有
String ,Int ,List<String>, List<Int>等
1. 属性类型是Int
, String
,Date
等类型
直接使用
setValue:forKey赋值
2. 属性类型是List<String>,List<Int>
等类型
现在我们要想办法 将
List<T>转换成
[T]( T 是基础类型) 先不着急傻瓜式的,一句句判断类型。我们分析下。
List<T>是realm中的
泛型集合结构体,跟数组类似,我们可以通过给
List<T>添加扩展方法来转成对应的
[T]类型
结合 1和2的情况 代码如下
private protocol _BaseValueProtocol { var _snsBaseValue:Any? { get} } extension List:_BaseValueProtocol { var _snsBaseValue: Any? { var values = [Element]() forEach { (value) in values.append(value) } return values } }基础部分实现代码 如下
if propertype == RLMPropertyType.object { //包含对象 以及数组(数组内容非基础数据类型) } else{ //objectValue 是 String ,Int ,List<String> List<Int> 等基础类型或者集合元素是基础类型的类型 if let realVaule = (objectValue as? _BaseValueProtocol)?._snsBaseValue { model.setValue(realVaule, forKey: name) }else{ model.setValue(objectValue, forKey: name) } }
属性是非基础类型
1.modelType:Any.Type
是NSObject
类型,我们可以利用给NSObject
添加的扩展创建对象
先修改一下我们的NSObject的扩展//createInstance private protocol _AutoMallocObjectProtocol { static func _creatInstance() -> Self } //MARK: - 实现协议 extension NSObject:_AutoMallocObjectProtocol { static func _creatInstance() -> Self { return self.init() } }创建代码
if propertype == RLMPropertyType.object { //包含对象 以及数组(数组内容非基础数据类型) if 对象 { let subm = (modelType:Any.Type as? _AutoMallocObjectProtocol.Type)?._creatInstance() ... //subm 递归调用当前方法给其属性赋值 model.setValue(subm, forKey: name) }else if 集合 { ... } }
2.modelType:Any.Type
是[T] ,(Array\<Element>)
等泛型集合类型
如何通过Any.Type类型创建此泛型集合呢
很明显,swift数组类型不是
NSObject类型,无法使用我们之前的扩展实现,它其实是个带泛型的struct类型,所以我们如果要创建对应数组必须知道该元素类型进行创建。我们可以让swift数组类型遵循我们的
_AutoMallocObjectProtocol协议 并添加默认实现 如下
extension Collection { static func _collectionCreatInstance() -> [Iterator.Element] { let instance = [Iterator.Element]() return instance } } //MARK: 集合类型 extension Array:_AutoMallocObjectProtocol { static func _creatInstance() -> Array<Element> { return _collectionCreatInstance() } } extension Set:_AutoMallocObjectProtocol { static func _creatInstance() -> Set<Element> { let arr = _collectionCreatInstance() return Set(arr) } }以上代码 顺便将set类型也遵循了创建协议并实现了,是一样的。所以这样我们可以通过下面代码来创建泛型集合啦
if propertype == RLMPropertyType.object { //包含对象 以及数组(数组内容非基础数据类型) if 对象 { ... }else if 集合 { let collections = (modelType:Any.Type as? _AutoMallocObjectProtocol.Type)?._creatInstance() //collections 中的该如何创建请继续看 } }继续思考接下来做什么
我们要创建这个数组里面的
元素
对象,并给元素
对象的所有属性赋值(通过递归)将这些
元素
对象加到collections
中。这样此collection
对象,它的内容也饱满了
到了这里我们又可以想一想问题,我们现在知道数组类型
modelType:Any.Type如何创建数组的
元素对象?,我们需要得到数组
元素的类型,怎么办呢?这个时候脑袋里蹦出四个字 扩展、协议。 我们定义一个创建
元素对象的协议,因为只有
泛型集合内部才知道其
Element类型,所以让
泛型集合遵循并实现。
//MARK:创建嵌套类型中的Instance private protocol _MallcoContentInstanceProtocol { static func _creatElement() -> Any? } //MARK: 集合类型 extension Array:_AutoMallocObjectProtocol,_MallcoContentInstanceProtocol { static func _creatInstance() -> Array<Element> { return _collectionCreatInstance() } static func _creatElement() -> Any? { if let _type = Element.self as? _AutoMallocObjectProtocol.Type { return _type._creatInstance() as? Element } return nil } } extension Set:_AutoMallocObjectProtocol,_MallcoContentInstanceProtocol { static func _creatInstance() -> Set<Element> { let arr = _collectionCreatInstance() return Set(arr) } static func _creatElement() -> Any? { if let _type = Element.self as? _AutoMallocObjectProtocol.Type { return _type._creatInstance() as? Element } return nil } }这样以来我们就可以通过如下代码创建数组中的元素类型对象了
if propertype == RLMPropertyType.object { //包含对象 以及数组(数组内容非基础数据类型) if 对象 { ... }else if 集合 { var collections = (modelType:Any.Type as? _AutoMallocObjectProtocol.Type)?._creatInstance() for m in objectValue { if let m = (modelType:Any.Type as? _MallcoContentInstanceProtocol.Type)?._creatElement() ... //m 进行递归kvc赋值 collections.append(m) } model.setValue(collections, forKey: name) } }泛型集合类型的创建问题差不多就解决了。
3.modelType:Any.Type
是[T]? ,(Array\<Element>?)
等可选非基本类型
到这里不知道有没有发现一个问题。上述集合类型,或者数组
元素中类型 可能不是
NSObject,也不是
集合类型,而是可选类型的非基本类型。
eg. modelType:Any.Type 类型是
[T]?也就是(
Option<Array<T>>),其中
T可能是个可选类型.
这个时候我们会发现可选类型并不遵循协议 就无法创建对象。怎么办?
按照上面的套路,我们继续给可选类型添加相关协议,并实现。
//MARK: 可选类型 extension Optional:_AutoMallocObjectProtocol,_MallcoContentInstanceProtocol { static func _creatInstance() -> Optional<Wrapped> { var instance:Wrapped if let _type = Wrapped.self as? _AutoMallocObjectProtocol.Type { instance = _type._creatInstance() as! Wrapped return Optional.init(instance) } return nil } static func _creatElement() -> Any? { if let _type = Wrapped.self as? _MallcoContentInstanceProtocol.Type { return _type._creatElement() } return nil } }对于
modelType:Any.Type属于
NSObject或者
泛型组合类型的情况。我们已经解决的差不多了。
总结
上面讲了
Realm.Object转换成普通业务
模型的实现过程,反过来普通业务
模型转换成
Realm.Object的实现与上面虽然有所区别,不过类似,就不在多说。
在当时写这转换的时候,所想的是方式方法都想要利用swift特性,这也是我想要说明的部分,这篇转换其实还有可以优化的地方,优化部分有兴趣可以联系我。
在整篇文章中我们所用到的转换协议
Persistable,创建对象协议
_AutoMallocObjectProtocol,创建嵌套类型中元素协议
_MallcoContentInstanceProtocol。其实就是swift面向协议编程
protocol+extension的具体体现。通过协议+扩展实现一个功能,能够定义所需要的充分必要条件,不多也不少。
swift相对于OC的传统协议,虽说只是多了一个协议扩展的特性,但却带来了编程范式的进化。实际上,Swift的标准库几乎是everything is starting out as a protocol。这也是为什么我想到了协议来做这些事情的原因。也许你还想看
探秘AFNetworking 2020-03-05
Swift Intermediate Language 初探 2020-01-09
关于NSObject对象的内存布局,看我就够了! 2019-12-19
加入搜狐技术作者天团
千元稿费等你来!
戳这里!☛
- swift学习笔记->类型转换,协议与扩展
- iOS CFObject和NSObject的相互转换(使用bridge)
- Swift-----类型转换 、 嵌套类型 、 扩展 、 协议 、 访问控制
- swift详解之十-------------异常处理、类型转换 ( Any and AnyObject )
- Swift AnyObject 类型转换
- Swift-----类型转换 、 嵌套类型 、 扩展 、 协议 、 访问控制
- 【iOS(swift)笔记-6】自定义ObjectMapper的时间转换
- swift详解之十-------------异常处理、类型转换 ( Any and AnyObject )
- FluorineFx ASObject自动转换基础类 AutoParseASObject ,用于Flash AMF协议解析
- Swift入门教程13-类型转换is as any anyobject
- NSObject class和NSObject protocol的关系(抽象基类与协议)
- Swift Any & AnyObject
- HTTP协议缓存策略深入详解之ETAG妙用
- Objective-C协议遵守NSObject协议的原因
- Object-C非正式协议与正式协议的区别
- JSONObject.toBean时多个类转换
- IOS开发语言Swift入门连载---类型转换
- swift把汉字转换为拼音,并且截取首字母做索引用
- [转载]category与protocol(Object-C 的非正式协议和正式协议)
- Swift中的协议(protocol)学习教程