2017-05-05 2 views
0

NSMangedObjectをデータベースから削除すると、割り当てられたローカル変数はどうなりますか?削除されたNSManagedObjectsへの参照を格納するローカル変数に何が起こるか

例えば、私はシンプルNSManagedObject持っている:

class MyManagedObject: NSManagedObject { 
    @NSManaged var name: String 
} 

をそして、私のViewControllerに、私はデータベースから引き出し、それをローカルに割り当てます

class ViewController: UIViewController { 
    var myManagedObject: MyManagedObject! 
} 

その後、私はそれを削除データベースから。

印刷する場合は、オブジェクト名のオブジェクトが存在しないかのように私は、コンソール

print("myManagedObject.name = \(myManagedObject.name)") 
//prints: "myManagedObject.name = " 

に次のように取得しますか?しかし、変数をオプションにしてnilをチェックすると、それはゼロでないと言われます。

私はこれを私の心の中でどうやって調和させるかについてはあまりよく分かりません。ローカル変数を指す何かがあるようですが、そのプロパティはなくなりました。

そのオブジェクトにそのオブジェクトのプロパティに依存する多くの異なるUIオブジェクトがある場合、そのオブジェクトのローカルディープコピーがメモリにあると想定できません。ここで


は、より完全なコードです:

のviewDidLoadで、私はそれをローカルに割り当て、その後、新しいオブジェクトを作成し、コンテキストを保存し、オブジェクトをフェッチします。今、私はデータベースから削除し、そう

print("The object's name is: \(myManagedObject.name)") 
//prints: The object's name is: My First Managed Object 

::私はローカル変数のnameプロパティを印刷する場合は、この時点で

class ViewController: UIViewController { 

     var myManagedObject: MyManagedObject! 

     override func viewDidLoad() { 
      super.viewDidLoad() 

      //1 Create the new object 
      let newObject = NSEntityDescription.insertNewObject(forEntityName: "MyManagedObject", into: coreDataManager.mainContext) as! MyManagedObject 
      newObject.name = "My First Managed Object" 

      //2 Save it into the context 
      do { 
       try coreDataManager.mainContext.save() 
      } catch { 
       //handle error 
      } 

      //3 Fetch it from the database 
      let request = NSFetchRequest<MyManagedObject>(entityName: "MyManagedObject") 
      do { 
       let saved = try coreDataManager.mainContext.fetch(request) 
       //4 Store it in a local variable 
       self.myManagedObject = saved.first 
      } catch { 
       //handle errors 
      } 
     } 
} 

が、私は正しい応答を得る

if let storedObject = myManagedObject { 
    coreDataManager.mainContext.delete(storedObject) 
    do { 
     try coreDataManager.mainContext.save() 
    } catch { 
     //handle error 
    } 
} 

しかし、今、私が印刷すると、最も奇妙な結果を得ます:

print("myManagedObject.name = \(myManagedObject.name)") 
//prints: "myManagedObject.name = " 

これはまったく私がメモリが動作することを期待している方法ではありません。クラスFooのインスタンスを作成し、そのインスタンスを別のオブジェクトに渡すと、同じインスタンスになります。それは誰もそれを指していないと消えるだけです。

この場合---変数は何ですか、myManagedObjectnilではありません。文字列は何ですか、name?それは空文字列ですか?それとも他の奇妙なメタタイプですか?

+0

ローカルに「割り当てる」コードを表示します。 var myManagedObject:MyManagedObject!あなたの質問の更新のためにMyManagedObject – Retterdesdialogs

+0

タイプの新しい変数を "作成"するだけです。管理対象オブジェクトを削除した後、削除済みとしてマークし、データベースを保存した後、データベースから削除します。管理対象オブジェクトでは、isDeletedプロパティまたは "fault"プロパティをチェックする必要があります。オブジェクトは予測どおりにオブジェクトを保持しますが、その動作は予測できません。フレームワークの変更に応じて何かが起きる可能性があり、空の文字列はそれがうまくいくための素晴らしい回避策のようです。このオブジェクトが削除された後にこのオブジェクトを使用しないことは、あなたの仕事です。 –

+0

しかし、ウサギの穴は深くなります。複数のコンテキストを使用し、あるコンテキストでオブジェクトを削除すると、そのオブジェクトは他のコンテキストにも存在します。しかし、第2の状況で変更を適用しようとすると、解決する必要がある競合が報告されます。したがって、メモリ内では、コンテキストごとに1つのインスタンスがあり、ARCルールが割り当てを解除するまで、これらのインスタンスはメモリ内に保持されます。しかし、そのプロパティはいつでも変更することができます。オブジェクトはまだ存在しますが、そのプロパティにはアクセスできません。あらゆる権利によってクラッシュするはずですが、例外が処理されたようです。 –

答えて

0

ここで主に考えているのは、コアデータのコンテキストです。コンテキストとは、メモリと実際のデータベースとの間の接続です。

データを取得するたびに、コンテキストでフェッチします。これらは、変更または削除することができる管理対象オブジェクトです。それでも文脈を保存するまでは、データベース上で実際には起こりません。

オブジェクトを削除すると、削除対象としてマークされますが、メモリから削除されません。オブジェクトが実際にデータベース自体から削除されるためには、コンテキストによってまだ使用されていないため、削除しないでください。

削除するように呼び出すと、管理対象オブジェクトはどうなるのですか?ドキュメント化されていても、フレームワークの一部として変更される可能性はありますが、それはほとんど無関係です。したがって、これらのケースをチェックし、必要に応じてオブジェクトを再フェッチするのはあなたの責任です。したがって、アプリケーションに適切なアーキテクチャがあり、コアデータを確実に使用するようにする必要があります。

データベースの使い方には非常に多くの方法があり、多かれ少なかれそれらの中に最適な方法を使用する独自の方法があります。あなたはあなたが何をしているのかを具体的に把握する必要があります。潜在的な問題がどこにあるのか分かりますので、正しい道を歩むことができます。

たとえば、リモートサーバーからのデータ同期について考えてみましょう。ここでは、ユーザーが何をしているのか、アプリケーションのどの部分に関係なく、いつでもデータを同期できます。

このため、別のスレッドで動作する単一のコンテキストを使用することをお勧めします。すべての管理オブジェクトはラップされ、そのプロパティはデータベースから取得された後にコピーされます。

myEntityInstance.commit() // Copies all the data to core data object. The operation is done on a context thread. A callback is usually not needed 
:あなたが直接あなたのような管理対象オブジェクトにデータをコピーする方法のいくつかの種類を必要とする管理対象オブジェクトへのアクセスを持っていないので、次に

MyEntity.findAll { items in 
    ...the fetch happens on context thread and returns to main, items are wrappers 
} 
MyEntity.find(id: idString, { item in 
    ...the fetch happens on context thread and returns to main, items are wrappers 
})() 

:ほとんどのエンティティでは、あなたのようなものを持っているでしょうデータベース

MyEntity.saveDatabse { 
    ... save happens on the context thread and callback is called on main thread 
} 

を保存する

そして今、スマートな部分はsaveDatabse方法は変更が行われているデリゲートに報告することです。デリゲートは通常現在のビューコントローラなので、ビュー上に表示されたDataBaseViewControllerのようなスーパークラスをデリゲートとして割り当てます。、ロード時にはreloadDataのメソッドが呼び出され、databaseDidChangeデリゲートメソッドではviewWillAppear

DataBaseViewControllerのサブクラスである各View Controllerは、reloadDataを上書きし、その方法でデータベースからデータを再度取得します。あなたはすべてのアイテムまたは単一アイテムをフェッチしています。ですから、それらのオブジェクトの場合は、オブジェクトのidを保存して、それを再度取得する必要があります。id返されたオブジェクトがnilの場合、アイテムが削除されているので、あなたが言及しているように見えます。

これらのことはすべて簡略化されていますが、コアデータとその使用方法についての基本的な考えを得ることを願っています。それは簡単ではない、それは決してなかったし、決してそうなることはないだろう。これは、可能な限り短時間で非常に大規模なデータベースからでもデータにアクセスできるように高速化されています。結果は非常に安全ではないかもしれないということです。

+0

「すべての管理対象オブジェクトをラップし、そのプロパティをデータベースから取得したらコピーする必要があります。 CoreDataを使用すると、データベースからオブジェクトを取得すると、実際にはすべてのプロパティをメモリ内の新しい管理されていないオブジェクトにコピーすることになりますか?したがって、すべてのクラスの2つのバージョンを作成する必要があります.1つは管理クラス、もう1つは管理されていないクラスです。 – MH175

+0

私は「はい」と書いた場合です。しかし、複雑なケースの場合は、一般的に必要なものではありません。最初は奇妙に見えるかもしれませんが、このオブジェクトはコアデータだけでなく、MVVMなどの複雑なシステムで使用できるサーバーデータ(json辞書)やUIモデルでもあり、完全にスレッドセーフであることがわかりますとても便利。このシステムの作成を検討している場合は、ほとんどの作業を行うスーパークラスが1つあることをお勧めします。サブクラスは、管理対象オブジェクトとの間で変換する2つのメソッドしか必要としません。コアデータはサブクラス化をサポートすることに注意してください –

関連する問題