コアデータモデルとマネージオブジェクトの違いを理解する

月にdonnywalsによって公開されました5, 2020

Xcodeがコアデータモデルファイルに基づいてNSManagedObjectクラスを生成するとき、管理オブジェクトのプロパティのほとんどはオプションであることに気づいた モデルエディタでそれらを必須にした場合でも、Xcodeはほとんどのプロパティがオプションである管理オブジェクトを生成します。

この記事では、この現象とそれがなぜ起こるのかを探ります。

生成されたNSManagedObjectサブクラスの探索

コアデータモデルにXcodeの自動コード生成を使用するプロジェクトをビルドすると、プロジェクトのビルド時にNSManagedObjectサブクラスが生成されます。 これらのクラスはプロジェクトの派生データフォルダに記述されているため、直接変更するべきではありません。 Xcodeによって生成されるモデルには、モデルエディタでプロパティをオプションにしたかどうかにかかわらず、エンティティに追加した一部のプロパテ

これは最初は奇妙に聞こえるかもしれませんが、実際にはそれほど奇妙ではありません。 次のNSManagedObjectサブクラスを見てみましょう:

extension ToDoItem { @nonobjc public class func fetchRequest() -> NSFetchRequest<ToDoItem> { return NSFetchRequest<ToDoItem>(entityName: "ToDoItem") } @NSManaged public var completed: Bool @NSManaged public var label: String?}

myToDoItemの2つのプロパティのうちの1つは、モデルエディタで両方とも必要であってもオプションです。 このToDoItemのインスタンスを作成するときは、次のコードを使用します:

let item = ToDoItem(context: managedObjectContext)

管理対象オブジェクトの初期化子は、管理対象オブジェクトコンテキストを取ります。 これは、ToDoItemの初期化中に管理プロパティに値を割り当てないことを意味します。 labelプロパティとcompletedプロパティの両方の値を印刷すると、結果が得られ、興味深い結果が得られます:

print(item.label) // nilprint(item.completed) // false

labelは予想どおりnilですが、Core Dataはデフォルト値falsecompletedプロパティに割り当てました。 さらに一歩進んで、次のコードを見てみましょう:

let item = ToDoItem(context: managedObjectContext)item.label = "Hello, item"print(item.label) // "Hello, item"print(item.completed) // falsedo { try managedObjectContext.save()} catch { print(error)}

このコードを実行すると、次の出力が生成されることがわかります:

Optional("Hello, item")falseError Domain=NSCocoaErrorDomain Code=1570 "completed is a required value." UserInfo={NSValidationErrorObject=<CoreDataExample.ToDoItem: 0x60000131c910> (entity: ToDoItem; id: 0x600003079900 <x-coredata:///ToDoItem/t1FABF4F1-0EF4-4CE8-863C-A815AA5C42FF2>; data: { completed = nil; label = "Hello, item";}), NSValidationErrorKey=completed, NSLocalizedDescription=completed is a required value.}

このエラーは、completedが設定されていないことを意味するcompleted is a required value.を明確に示し、エラーメッセージと一緒に表示される印刷された管理オブジェクトもcompletednilであるこ したがって、completedには何らかの種類のデフォルト値が存在しますが、明示的に割り当てられるまでは非nilとはみなされません。

何が起こっているのかを理解するために、completedに値を割り当て、itemの印刷された説明をもう一度見てみることができます:

let item = ToDoItem(context: managedObjectContext)item.label = "Hello, item"print(item.completed)print(item)

このコードは、次の出力を生成します:

false<CoreDataExample.ToDoItem: 0x6000038749b0> (entity: ToDoItem; id: 0x600001b576e0 <x-coredata:///ToDoItem/tD27C9C9D-A676-4280-9D7C-A1E154B2AD752>; data: { completed = 0; label = "Hello, item";})

これはかなり興味深いですね。

completedプロパティはBoolとして定義されていますが、数値として出力されます。 この理由は、基礎となるSQLiteストアで見つけることができます。 Core Data storeのSQLiteファイルを調べる最も簡単な方法は、アプリの起動引数として-com.apple.CoreData.SQLDebug 1を渡し、Core DataがSQLiteデータベースブラウザなどのSQLiteエクスプローラで接続するSQLite

ヒント:
コアデータ起動引数の詳細については、この記事を参照してください。

ZTODOITEMのスキーマ定義を見ると、ZCOMPLETEDの型としてINTEGERが使用されていることがわかります。 これは、completedプロパティが基になるSQLiteストアに整数として格納されていることを意味します。 completedINTEGERとして格納される理由は簡単です。 SQLiteにはBOOLEAN型がなく、代わりにfalseを表すためにINTEGER値0を使用し、代わりにtrueを表すために1を使用します。マネージオブジェクトインスタンスを印刷するときに表示されるdataは、completedプロパティの値ではなく、SQLiteストアに書き込まれるcompletedの値です。

この節から学ぶべきことは2つあります。

まず、定義されたコアデータモデルと生成された管理対象オブジェクトのオプションの間に不一致があることがわかりました。 非オプションのStringは、生成されたモデルではオプションのStringとして表され、非オプションのBoolは、生成されたモデルではオプションのBoolとして表されます。

次に、マネージオブジェクトモデルでの値の表現方法と、基になるSQLiteストアでの値の表現方法に違いがあることを学びました。 マネージオブジェクトインスタンスを基になるストレージに書き込むために使用される値を確認するには、マネージオブジェクトを印刷し、印刷出力でdata

ここでの主な教訓は、モデルエディタのコアデータモデルとマネージオブジェクトサブクラスがデータを同じように表現しないことです。 コアデータモデルでのOptionalは、マネージオブジェクトサブクラスでは常にoptionalを意味するとは限らず、その逆もあります。 コアデータモデル内の省略可能でない値は、マネージオブジェクトサブクラス内の省略可能な値として表される場合があります。 コアデータは、永続ストアに書き込むときに管理対象オブジェクトをその管理対象オブジェクトモデルに対して検証し、検証エラーが発生した場合はエ

では、なぜこの不一致が存在するのですか? マネージオブジェクトモデルとマネージオブジェクトサブクラスに直接マッピングがある場合は、はるかに簡単ではないでしょうか?

マネージオブジェクトとコアデータモデルの不一致の理解

マネージオブジェクトとモデルエディタで定義したモデルの間に不一致がある理由の大部分は、Core DataのObjective-Cのルーツから来ています。Objective-CはOptionalをまったく扱っていないので、モデル定義からSwiftコードへの適切なマッピングは必ずしもありません。 多くの場合、マッピングの動作方法はやや恣意的なようです。 たとえば、Optional<String>Optional<Bool>は、OptionalがObjective-Cに存在しないという単純な理由で、Objective-cの型として表すことはできません。 残念ながら、Optional<Bool>は、@NSManagedプロパティをBool?として定義しようとするとXcodeが通知するため、Objective-Cの何かに自動的にマップすることはできません。Objective-Cで作業したことがない場合、Optionalという概念がないことは非常に奇妙に思えるかもしれません。 Swiftの前にCore Dataでオプションのプロパティをどのように使用しましたか? そして、Objective-Cで何かがnilになるとどうなりますか?Objective-Cでは、期待していない場合でも、値がnilであることは完全に問題ありません。 コアデータはObjective-Cにルーツを持っているので、このレガシーのいくつかは、時には理想的ではない方法で生成されたSwiftクラスに引き継がれます。

ここで最も重要なのは、Objective-Cの仕組みやXcodeがコードを正確に生成する方法ではありません。 代わりに、コアデータモデル定義の型と構成が、(生成された)管理対象オブジェクトのサブクラスの型と一致しないことを覚えておいてください。

まとめ

今週の記事では、マネージオブジェクトサブクラスとコアデータモデル定義が必ずしも期待どおりに並んでいるとは限らないことにつ モデルエディターのオプション以外のプロパティは、生成されたマネージオブジェクトサブクラスでオプションとして終わることがあり、それ以外の場合は、デフォルト値を自分で割り当てていなくても、デフォルト値を持つオプション以外のプロパティとして終わることがあります。

また、デフォルト値がマネージオブジェクトインスタンスに存在する場合、Core Data modelエディターで明示的にデフォルト値を定義しない限り、マネージオブジェク

これは確かに混乱して不幸ですが、Core Dataは管理オブジェクトの保存中にスローされるエラーの何が間違っているかを伝えるのにかなり優れています。 また、コアデータが格納しようとする値を検査するには、マネージオブジェクトインスタンスを印刷し、そのdata属性を検査します。

個人的なメモとして、今週の記事で説明した動作は、コアデータの将来のアップデートで対処され、マネージオブジェクトサブクラスがモデルエディタで定義されているコアデータモデルに近い、おそらく直接マッピングされている場合に、より迅速にフレンドリーになることを願っています。 しかし、それまでは、モデルエディタとマネージオブジェクトサブクラスが同じ方法でモデルを表すのではなく、これがコアデータのObjective-Cルートに少なくとも

この投稿に関する質問、修正、フィードバックがあればTwitterでお知らせください。 この記事は、私が取り組んでいるコアデータに関する本のためにやっている研究、探査、準備の一部です。 この本についての更新については、Twitterで私に従うことを確認してください。 私は現在、2020年の終わり頃に本をリリースする予定です。

私の毎週のニュースレターを最新の状態に滞在

Practical Combine

Combineについて知っておく必要があるすべてを学び、私の新しい本Practical Combineであなたのプロジェクトでそれをどのように使用できるかを学びます。 あなたは13章、遊び場とあなたができるだけ早くCombineを起動して実行するのを助けるためにサンプルプロジェクトの一握りを取得します。

この本はちょうど2 29.99のためのデジタルダウンロードとして利用可能です!

コメントを残す

メールアドレスが公開されることはありません。