2017-05-05 1 views
0

Lorsque je supprime un NSMangedObject de la base de données, qu'advient-il des variables locales qui lui ont été affectées?Qu'arrive-t-il aux variables locales qui stockent des références à des fichiers NSManagedObjects supprimés?

Par exemple, j'ai simple NSManagedObject:

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

Et puis dans mon ViewController, je le tire hors de la base de données, et l'affecter localement:

class ViewController: UIViewController { 
    var myManagedObject: MyManagedObject! 
} 

Je le supprimer de la base de données.

Si imprimer le nom de l'objet que je reçois le texte suivant dans la console

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

Comme si l'objet n'est pas là? Mais si je transforme la variable en option et la vérifie pour rien, on me dit que ce n'est pas nul.

Je ne sais pas comment concilier cela dans mon esprit. Il semble y avoir quelque chose qui pointe vers la variable locale, mais ses propriétés ont disparu.

Si j'ai de nombreux objets IU disparates qui dépendent de cet objet pour ses propriétés, je ne peux pas supposer qu'il existe une copie profonde locale de celui-ci en mémoire?


est ici code plus complet:

En viewDidLoad je crée le nouvel objet, sauvegarder le contexte, chercher l'objet, puis l'affecter localement.

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 
      } 
     } 
} 

À ce stade, si j'imprime la propriété variable locale de name, je reçois la réponse correcte:

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

Alors, maintenant, je le supprimer de la base de données:

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

Mais maintenant, quand j'imprime je reçois la sortie la plus étrange:

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

Ce n'est vraiment pas la façon dont je m'attends à ce que la mémoire fonctionne. Si je crée une instance d'une classe Foo, puis passez cette instance à différents objets, c'est la même instance. Il ne disparaît qu'une fois que personne ne l'indique.

Dans ce cas --- quelle est la variable, myManagedObject? Ce n'est pas nil. Et quelle est la chaîne, name? Est-ce une chaîne vide? Ou est-ce un autre méta-type bizarre?

+0

Afficher le code où vous "l'attribuez" localement. var myManagedObject: MyManagedObject! va juste "faire" une nouvelle variable de type MyManagedObject – Retterdesdialogs

+0

En raison de la mise à jour de votre question: Après avoir supprimé l'objet géré, vous l'avez marqué comme supprimé et après l'avoir sauvegardé, vous le supprimez de la base de données (pas en mémoire). Sur l'objet géré, vous devriez vérifier la propriété isDeleted ou même la propriété "fault". L'objet persiste dans la mémoire comme vous l'avez prévu mais son comportement est dans votre cas imprévisible.Tout peut arriver en fonction des changements dans le cadre et la chaîne vide semble être une bonne solution pour que cela fonctionne encore. C'est votre travail de ne pas utiliser cet objet après qu'il a été supprimé. –

+0

Mais le trou de lapin va plus loin. Si vous utilisez plusieurs contextes et que vous supprimez l'objet dans un contexte, il existera toujours dans l'autre. Mais une fois que vous essaierez d'appliquer des changements dans le second contexte, un conflit sera signalé, que vous devrez résoudre. Ainsi, en mémoire, vous aurez une instance par contexte et ces instances seront conservées en mémoire jusqu'à ce que les règles d'ARC décident de la libérer. Mais ses propriétés peuvent changer à tout moment. Ergo l'objet existe toujours mais ses propriétés sont inaccessibles. Par tous les droits, il devrait tomber en panne, mais il semble que l'exception a été traitée pour vous. –

Répondre

0

La principale chose que vous recherchez probablement est le contexte de données de base. Le contexte est une connexion entre votre mémoire et la base de données actuelle.

Chaque fois que vous récupérez les données, vous les récupérez via le contexte. Ce sont des objets gérés qui peuvent être modifiés ou même supprimés. Cependant, rien de tout cela ne se passe réellement dans la base de données tant que vous n'avez pas sauvegardé le contexte.Lorsque vous supprimez un objet, il est marqué pour la suppression, mais il n'est pas supprimé de la mémoire, il ne doit pas être utilisé car il sera toujours utilisé par le contexte pour supprimer réellement l'objet de la base de données elle-même. Qu'arrive-t-il à l'objet géré une fois que vous l'avez appelé pour le supprimer n'est pas vraiment pertinent, même s'il est documenté, il peut changer car il fait partie de l'infrastructure. Il est donc de votre responsabilité de vérifier ces cas et de réapprovisionner les objets une fois nécessaires. Vous devez donc vous assurer que votre application possède une architecture appropriée et utilise les données de base de manière responsable.

Il existe de nombreuses façons d'utiliser votre base de données et plus ou moins d'entre elles ont une façon unique de l'utiliser de façon optimale. Vous devez être plus précis sur ce que vous faites et où voyez-vous les problèmes potentiels afin que nous puissions vous mettre sur la bonne voie.

Pour vous donner un exemple, tenez compte de la synchronisation des données du serveur distant. Ici, vous vous attendez à ce que les données puissent être synchronisées à tout moment, peu importe ce que fait l'utilisateur ou quelle partie de l'application il est. Pour cela, je suggère que vous ayez un seul contexte qui fonctionne sur un thread séparé. Tous les objets gérés doivent être encapsulés et leurs propriétés copiées une fois extraites de la base de données. Sur la plupart des entités que vous auriez quelque chose comme:

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 
})() 

Alors, puisque vous n'avez pas accès à l'objet géré dont vous avez besoin directement une sorte de méthode pour copier les données sur l'objet géré comme:

myEntityInstance.commit() // Copies all the data to core data object. The operation is done on a context thread. A callback is usually not needed 

Et puis pour sauvegarder la base de données

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

maintenant, la partie intelligente est cette méthode saveDatabse fera rapport à un délégué que des modifications ont été apportées. Un délégué est généralement le contrôleur de vue en cours donc il est logique d'avoir une superclasse comme DataBaseViewController qui apparaissait sur la vue comme un délégué MyEntity.delegate = self, à la vue did load appelle une méthode reloadData et dans les appels de méthode databaseDidChange de délégué reloadData et même dans viewWillAppear . Maintenant, chacun de vos contrôleurs de vue qui sont sous-classe de DataBaseViewController remplacera le reloadData et dans cette méthode vous récupérerez à nouveau les données de la base de données. Soit vous allez chercher tous les objets ou un seul. Donc, pour ceux qui sont singe, vous devez enregistrer le id de l'objet et le récupérer à nouveau par id. Si l'objet renvoyé est nil, l'élément a été supprimé afin que vous puissiez voir le problème que vous semblez mentionner. Toutes ces choses sont simplifiées à l'extrême mais j'espère que vous aurez une idée de base sur les données de base et comment les utiliser. Ce n'est pas facile, ça n'a jamais été et ça ne le sera probablement jamais. Il est conçu pour être capable d'accéder aux données même à partir d'une très grande base de données dans les plus brefs délais. Le résultat est que cela pourrait ne pas être très sûr.

+0

Lorsque vous dites: "Tous les objets gérés doivent être encapsulés et leurs propriétés copiées une fois extraites de la base de données." Voulez-vous dire que lorsque vous utilisez CoreData, une fois que vous récupérez vos objets de la base de données, vous copiez toutes leurs propriétés dans de nouveaux objets non gérés en mémoire? Vous devez donc créer deux versions de toutes vos classes: une gérée et une non gérée. – MH175

+0

Dans le cas où j'ai décrit Oui. Mais c'est pour le complexe des cas, ce n'est pas quelque chose dont vous avez généralement besoin. Cela peut sembler étrange au premier abord mais une fois que vous voyez que ces objets ne sont pas seulement pour les données de base mais également pour les données de serveur (dictionnaires json) et le modèle UI qui peut être utilisé pour des systèmes complexes tels que MVVM. très pratique. Si vous envisagez de créer ce système, je suggère que vous ayez une superclasse qui effectue la majeure partie du travail, alors les sous-classes n'ont besoin que de 2 méthodes de conversion depuis et vers l'objet géré. Notez que les données de base prennent en charge le sous-classement –