概述
各位似禿似不禿小碼農們都知道,在蘋果眾多開發平臺中 CoreData 無疑是那個最簡潔、擁有“官方認證”且最具兼容性的數據庫框架。使用它可以讓我們非常方便的搭建出 App 所需要的持久存儲體系。
不過,大家是否知道在 CoreData 中還存在一個 Transformable 類型,它到底是個啥?應用場景有哪些?在最新的 SwiftData 中有沒有對應物?對于開發者又有哪些“見雀張羅”的擼碼陷阱和最佳實踐呢?
在本篇博文中,您將學到如下內容:
- 概述
- 6. 派生自 NSObject 的自定義類
- 6.1 為 CoreData 中 Human 基類增加 skill 字段
- 6.2 實現 Skill 數據結構
- 總結
本系列文章一共包括將近 3w 枚機智而幽默的文字、詳實的大段代碼示例以及海量圖片,定能讓小伙伴們對 Transformable 類型的“駕馭”更加胸有成竹、勝券在握!
那還等什么呢?讓我們馬上開始 Transformable 大冒險吧!
Let’s go!!!😉
6. 派生自 NSObject 的自定義類
之前我們討論了系統內置類、自定義結構等數據類型對于 Transformable 的支持,接下來,讓我們再聊聊如何穩妥的處理自定義類的轉換。
如若要我們自己的類也安全地支持 Transformable 類型,有兩個條件:
- 必須派生自 NSObject 類;
- 必須遵守 NSSecureCoding 協議;
下面,我們就來實際擼碼一回,看看具體的步驟是怎樣的。
為了進一步拓展我們英雄和惡棍的戰斗力,我們想為它們增加**技能(Skill)**這一超能力。我們這次希望用類的多態機制來適配不同種類 Human(英雄或惡棍)的 Skill。
6.1 為 CoreData 中 Human 基類增加 skill 字段
俗話說得好:“高樓平地起,基礎不牢地動山搖”,我們首先要為 CoreData 中 Humam 基類增加支持 Skill 的字段:
從上圖可以看到,我們主要做了以下幾件事:
- 為 Human 托管基類增加 skill 字段,它的類型為 Transformable;
- 設置 skill 字段的 Transformer 屬性為 SkillTransformer(待實現);
- 設置 skill 字段的 Custom Class 屬性為 Skill(待實現);
這里再啰嗦幾句,Transformable 字段的 Transformer 屬性存放的是該字段實際類型到 Transformable 類型的轉換器,而 Custom Class 則代表該字段的實際類型。
6.2 實現 Skill 數據結構
前面說過,我們希望英雄和惡棍的技能可以利用類的多態性來擴展,所以首先我們創建一個 Skill 抽象基類:
@objc(Skill)
public class Skill: NSObject, NSSecureCoding {let name: Stringinit(name: String) {if type(of: self) == Skill.self {fatalError("Skill 是抽象基類,禁止實例化!")}self.name = namesuper.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 Stringsuper.init()}
}
在 Skill 抽象基類的實現中,我們有幾點需要注意:
- 抽象基類應該禁止實例化,因為 Swift 語言本身沒有提供靜態的禁止方法,所以我們只能通過動態判斷來達成;
- Skill 內部應該存放所有子類共享的數據,比如 name;
- Skill 需要實現 supportsSecureCoding 屬性并返回 true,表示它支持安全編碼;
- Skill 應該實現 NSSecureCoding 協議所要求的兩個方法,它們分別被用來做數據的編碼和解碼工作;
- Skill 的類名稱需要用 @objc 關鍵字將 Swift 的類名暴露給 objc 層,否則運行會提示找不到名為 Skill 的類;
隨后,我們需要分別實現英雄和惡棍的技能,它們應該是 Skill 的子類:
/// 英雄技能
@objc(HeroSkill)
class HeroSkill: Skill {static let glare = HeroSkill(name: "英雄怒視", justicePower: 109, color: .pink)let justicePower: Intlet color: Colorinit(name: String, justicePower: Int, color: Color) {self.justicePower = justicePowerself.color = colorsuper.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: Intinit(name: String, evalPower: Int) {self.evalPower = evalPowersuper.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 的轉換,你值得擁有!
感謝觀賞,我們下一篇再會啦!😎