变幻莫测:CoreData 中 Transformable 类型面面俱到(六)

概述

各位似秃似不秃小码农们都知道,在苹果众多开发平台中 CoreData 无疑是那个最简洁、拥有"官方认证"且最具兼容性的数据库框架。使用它可以让我们非常方便的搭建出 App 所需要的持久存储体系。

不过,大家是否知道在 CoreData 中还存在一个 Transformable 类型,它到底是个啥?应用场景有哪些?在最新的 SwiftData 中有没有对应物?对于开发者又有哪些"见雀张罗"的撸码陷阱和最佳实践呢?

在本篇博文中,您将学到如下内容:

  • 概述
  • [6. 派生自 NSObject 的自定义类](#6. 派生自 NSObject 的自定义类)
    • [6.1 为 CoreData 中 Human 基类增加 skill 字段](#6.1 为 CoreData 中 Human 基类增加 skill 字段)
    • [6.2 实现 Skill 数据结构](#6.2 实现 Skill 数据结构)
  • 总结

本系列文章一共包括将近 3w 枚机智而幽默的文字、详实的大段代码示例以及海量图片,定能让小伙伴们对 Transformable 类型的"驾驭"更加胸有成竹、胜券在握!

那还等什么呢?让我们马上开始 Transformable 大冒险吧!

Let's go!!!😉


6. 派生自 NSObject 的自定义类

之前我们讨论了系统内置类、自定义结构等数据类型对于 Transformable 的支持,接下来,让我们再聊聊如何稳妥的处理自定义类的转换。

如若要我们自己的类也安全地支持 Transformable 类型,有两个条件:

  1. 必须派生自 NSObject 类;
  2. 必须遵守 NSSecureCoding 协议;

下面,我们就来实际撸码一回,看看具体的步骤是怎样的。

为了进一步拓展我们英雄和恶棍的战斗力,我们想为它们增加**技能(Skill)**这一超能力。我们这次希望用类的多态机制来适配不同种类 Human(英雄或恶棍)的 Skill。

6.1 为 CoreData 中 Human 基类增加 skill 字段

俗话说得好:"高楼平地起,基础不牢地动山摇",我们首先要为 CoreData 中 Humam 基类增加支持 Skill 的字段:

从上图可以看到,我们主要做了以下几件事:

  1. 为 Human 托管基类增加 skill 字段,它的类型为 Transformable;
  2. 设置 skill 字段的 Transformer 属性为 SkillTransformer(待实现);
  3. 设置 skill 字段的 Custom Class 属性为 Skill(待实现);

这里再啰嗦几句,Transformable 字段的 Transformer 属性存放的是该字段实际类型到 Transformable 类型的转换器,而 Custom Class 则代表该字段的实际类型。

6.2 实现 Skill 数据结构

前面说过,我们希望英雄和恶棍的技能可以利用类的多态性来扩展,所以首先我们创建一个 Skill 抽象基类:

swift 复制代码
@objc(Skill)
public class Skill: NSObject, NSSecureCoding {
    
    let name: String

    init(name: String) {
        
        if type(of: self) == Skill.self {
            fatalError("Skill 是抽象基类,禁止实例化!")
        }
        
        self.name = name
        super.init()
    }
    
    public class var supportsSecureCoding: Bool { true }
    
    public func encode(with coder: NSCoder) {
        coder.encode(name, forKey: "name")
    }
    
    required public init(coder: NSCoder) {
        name = coder.decodeObject(of: NSString.self, forKey: "name")! as String
        super.init()
    }
}

在 Skill 抽象基类的实现中,我们有几点需要注意:

  • 抽象基类应该禁止实例化,因为 Swift 语言本身没有提供静态的禁止方法,所以我们只能通过动态判断来达成;
  • Skill 内部应该存放所有子类共享的数据,比如 name;
  • Skill 需要实现 supportsSecureCoding 属性并返回 true,表示它支持安全编码;
  • Skill 应该实现 NSSecureCoding 协议所要求的两个方法,它们分别被用来做数据的编码和解码工作;
  • Skill 的类名称需要用 @objc 关键字将 Swift 的类名暴露给 objc 层,否则运行会提示找不到名为 Skill 的类;

随后,我们需要分别实现英雄和恶棍的技能,它们应该是 Skill 的子类:

swift 复制代码
/// 英雄技能
@objc(HeroSkill)
class HeroSkill: Skill {
    static let glare = HeroSkill(name: "英雄怒视", justicePower: 109, color: .pink)
    
    let justicePower: Int
    let color: Color
    
    init(name: String, justicePower: Int, color: Color) {
        self.justicePower = justicePower
        self.color = color
        super.init(name: name)
    }
    
    override class var supportsSecureCoding: Bool { true }
    
    public override func encode(with coder: NSCoder) {
        coder.encode(justicePower, forKey: "justicePower")
        let uiColor = UIColor(color)
        coder.encode(uiColor, forKey: "uiColor")
        super.encode(with: coder)
    }
    
    required public init(coder: NSCoder) {
        justicePower = coder.decodeInteger(forKey: "justicePower")
        let uiColor = coder.decodeObject(of: UIColor.self, forKey: "uiColor")!
        color = Color(uiColor: uiColor)
        super.init(coder: coder)
    }
}

/// 恶棍技能
@objc(VillainSkill)
class VillainSkill: Skill {
    
    static let swallow = VillainSkill(name: "恶棍吞咽", evalPower: 121)
    
    let evalPower: Int
    
    init(name: String, evalPower: Int) {
        self.evalPower = evalPower
        super.init(name: name)
    }
    
    override class var supportsSecureCoding: Bool { true }
    
    public override func encode(with coder: NSCoder) {
        coder.encode(evalPower, forKey: "evalPower")
        super.encode(with: coder)
    }
    
    required public init(coder: NSCoder) {
        evalPower = coder.decodeInteger(forKey: "evalPower")
        super.init(coder: coder)
    }
}

对于上面 HeroSkill 和 VillainSkill 两个子类,我们实现了它们各自具体的数据编码和解密功能,并为它们分别创建了一个实例(glare 和 swallow)。

回想一下,之前我们为 Human.skill 字段的 Custom Class 设置的是 Skill 基类,而不是具体的子类,这意味着具体 Skill 子类的编码和解密工作都会由系统自动帮你搞定,真是妙哉妙哉!

在下一篇博文中,我们将继续完成 Skill 到 Transformable 类型的转换,等着你们哦!

总结

在本篇博文中,我们初步介绍了派生自 NSObject 的自定义类如何支持 Transformable 的转换,你值得拥有!

感谢观赏,我们下一篇再会啦!😎

相关推荐
DARLING Zero two♡10 天前
C++显性契约与隐性规则:类型转换
c++·类型转换
大熊猫侯佩10 天前
Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(上)
swift·协议·protocol·coredata·协议扩展·托管基类·协议关联类型
大熊猫侯佩10 天前
Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
swift·协议·protocol·coredata·协议扩展·基类子类·类型不匹配
数造科技1 个月前
数造科技携 DataBuilder 亮相安徽科交会,展现“DataOps +AI”双引擎魅力
大数据·人工智能·科技·ai·业界资讯·data
大熊猫侯佩2 个月前
“群芳争艳”:CoreData 4 种方法计算最大值的效率比较(上)
swift·排序·sort·array·coredata·nsarray·最大值 max
凳子花❀3 个月前
探秘基带算法:从原理到5G时代的通信变革【八】QAM 调制 / 解调
5g·编码解码·调制解调·基带算法·qam调制/解调
凳子花❀3 个月前
探秘基带算法:从原理到5G时代的通信变革【五】CORDIC算法
5g·编码解码·调制解调·基带算法·cordic算法
凳子花❀4 个月前
探秘基带算法:从原理到5G时代的通信变革【九】QPSK调制/解调
5g·编码解码·调制解调·基带算法·qpsk调制/解调
凳子花❀4 个月前
探秘基带算法:从原理到5G时代的通信变革【四】Polar 编解码(二)
5g·编码解码·调制解调·基带算法·polar编解码