コアデータモデルの更新ああ.


CoreDataを使用して任意の時間を費やしている場合は、意見があります.
個人的には大好きです.それはグラフィカルなモデリングとの関係の解像度を持つ単純な永続層です.現在NSPersistentContainer APIは、コードのわずか数行で紡績することができます.それを明らかにそれを作るチームは、毎年、また、改善の新鮮な収穫をもたらします.
あなたはcoredataがSQLite データベース.デフォルトのモードでは、データをSQLite 単なる実装の詳細です.CoreDataもリレーショナルデータベースではないので、1つのように扱うことはありません.
最もベテラン開発者にも恐怖を打つことができる1つの活動はあなたのアップグレードですxcdatamodel . プロパティの追加のような単純な変更は非常に簡単ですが、関係を変更するような複雑な変更は難しい場合は特に対処するために複数のモデルのバージョンを持つことができます.誰もクライアントデータを失いたくない.
実際には、あなたの永続的なストアにアップグレードトグルを使用して、自動化されたモデルのアップグレードシステムを使用して、ほとんど簡単かつ透明です.
let container = NSPersistentContainer(name: "Unicorn")
let url = //url to store file
let store = NSPersistentStoreDescription(url: url)
store.shouldMigrateStoreAutomatically = true
store.shouldInferMappingModelAutomatically = true
container.persistentStoreDescriptions = [store]
container.loadPersistentStores { (storedesc, error) in
     //do data dependant setup
}
あなたは設定できますshouldMigrateStoreAutomatically & shouldInferMappingModelAutomatically to true そして、あなたの時代に進んでください.
どのように新しいあなたのモデルを更新せずに対処するxcdatamodel バージョン?あなたが事前に準備する場合は、恐ろしいモデルのアップグレードを避けるためにあなたのエンティティに部屋をちらりと少し追加することができます.
あなたはBucket of Goo ™ テクニック.エンティティ内の場所を追加してジェネリックを格納しますDictionary とその辞書の合成プロパティを格納します.これはかなりの反パターンではありませんが、技術的な負債の形で直接的なコストがあるので、それは非常にスケーラブルではありません.それでも、それは一時的な穴からあなたを得るかもしれません.
最初にすることはmetadata あなたの属性Entity 活字Data . また、なぜあなたが使用したくない参照してくださいよString 型として.

自動生成されたクラスファイルか、自分で作成したもののどちらかがこのようになります
class Thing: NSManagedObject {

    @NSManaged var name: String?
    @NSManaged var index: Int16
    @NSManaged var metadata1: String?
    @NSManaged var metadata2: Data?
}
すべてのプロパティは@NSManaged オブジェクトCランタイムとKvo力学にそれらをバインドする.
次に、合成辞書に簡単にアクセスを提供します.
var metadataDictionaryString: SwiftDict {
    get {
        let string = metadata1
        if let dict = string?.propertyList() as? SwiftDict {
            return dict
        }
        return [:]
    }
    set {
        let string = (newValue as NSDictionary).description
        metadata1 = string
    }
}
このスニペットを使わないでください.それはあなたが私を過去ばか何を示して含まれていた.それは事実に依拠しているdescription 有効な生成plist for NSString , NSDictionary and NSArray . thatsもはやそのようなケースとタイプData 実行時にクラッシュします.
合成プロパティを実装する方法はData バケットタイプとPropertyListSerialization 往復する.
var metadataDictionaryData: SwiftDict {
    get {
        if let data = metadata2 {
            do {
                let dict = try PropertyListSerialization.propertyList(from: data, options: [], format: nil) as? SwiftDict
                return dict ?? [:]
            }
            catch {
                print("plist decode err - ",error)
            }
        }
        return [:]
    }
    set {
        do {
            let data = try PropertyListSerialization.data(fromPropertyList: newValue, format: .binary, options: 0)
            metadata2 = data
        }
        catch {
            print("plist encode err - ",error)
        }
    }
}
次のステップは、新しい予期しない属性を合成することです.レットコールextendedName .
extension Thing {

    @objc var extendedName: String? {
        get {
            return metadataDictionaryData["extendedName"] as? String
        }
        set {
            metadataDictionaryData["extendedName"] = newValue
        }
    }

}
これで、あなたのモデルに新しい属性がありますxcdatamodel 世代

費用はいくらですか。


コストは
  • 使用する時間.それぞれの要求は、シャッフルする必要がありますData ブロブ.
  • CPUサイクル= =消費電力.
  • 経由でアクセスできないNSFetchRequest , I . E検索できません.
  • 可能な技術負債は最初に正しくそれをしていないから.
  • MacOSのショートテストを受けることができます
    アクセスして更新するname エンティティのプロパティですThing .
    0.00485 s to add 1000 items
    0.00513 s to update 1000 items
    0.00657 s to read 1000 items
    
    アクセスして更新するextendedName これはmetadata2
    0.02022 s to add extended 1000 items
    0.03116 s to update extended 1000 items
    0.01254 s to read extended 1000 items
    
  • 追加操作は〜5倍高価です.
  • 更新は〜6倍より高価です.
  • 読むよりも2倍高価です.
  • これを軽減できますか。


    多くの状況を読むには、キャッシュを使用することができます.しかし、これはあなたの記憶のフットプリントを増加させます.
    fileprivate var metadata2Cache: SwiftDict?
    
    var metadataDictionaryData: SwiftDict {
        get {
            if let cache = metadata2Cache {
                return cache
            }
            if let data = metadata2 {
                ...
            }
            return [:]
    
        }
        set {
            do {
                let data = ...
                metadata2 = data
                metadata2Cache = nil
            }
            catch {
                print("plist encode err - ",error)
            }
        }
    }
    
    キャッシュを最初にロードするget そして、任意の上でそれを燃やすset .
    その違いを見てみなさい.
    **0.02022 s to add extended 1000 items
    **0.03116 s to update extended 1000 items
    **0.01254 s to read extended 1000 items
    **0.00972 s to read extended 1000 items
    **0.00840 s to read extended 1000 items
    
    最初に読む必要がデコードするData BLOBが、その後の読み取りはキャッシュから出て、約1.25 xのネイティブ属性よりも高価です.

    結論


    属性を合成版にダンプすることによってモデルのアップグレードを避けることができますが、処理コストに関して大きな欠点があります.このテクニックは、パフォーマンスに敏感なループに適していないでしょう.
    あなたはこのテクニックで穴から自分を掘ることができるかもしれないが、長期的な拡張戦略としてそれに依存しないでください.新しいモデルの世代を作成します.