Je construis ma première application iOS, qui doit être assez simple, mais j'ai des difficultés à le faire suffisamment de balle pour que je me sente confiant dans l'App Store.
Brièvement, l'écran principal a Une vue sur la table, lors de la sélection d'une ligne, il segendra à une autre vue de la table qui affiche des informations pertinentes pour la ligne sélectionnée de manière maîtrise. Les données sous-jacentes sont extraites sous forme de données JSON d'un service Web une fois par jour, puis mis en cache dans un magasin de données de base. Les données précédentes à ce jour sont supprimées pour arrêter le fichier de base de données SQLite de la croissance indéfiniment. Toutes les opérations de persistance des données sont effectuées à l'aide de données de base, avec un Le problème que je vois est que si vous passez rapidement entre les écrans de maître et de détail Plusieurs fois, tandis que des données fraîches sont récupérées, analysées et sauvegardées, l'application gèle ou se bloque complètement. Il semble y avoir une sorte de problème de race, peut-être dû à des données de base d'importation de données en arrière-plan tandis que le fil principal tente d'effectuer une récupération, mais je spécule. J'ai eu du mal à capturer des informations de collision significatifs, il s'agit généralement d'un SIGSEGV au fond de la pile de données principale. P> Le tableau ci-dessous montre l'ordre réel des événements qui se produisent lorsque le contrôleur de vue de la table de détail est chargé: une fois que le bloc d'achèvement de l'opération Afnet est déclenché lorsque les données JSON sont arrivées, un nsfetchedresulttroller code> sous-aller la vue de la table de détail. P>
nsmanagedObjectContext code> est créé et transmis à un objet "importateur" qui analyse Les données JSON et enregistre les objets dans le magasin de données de base. L'importateur s'exécute à l'aide de la nouvelle méthode code> méthode introduite dans iOS 5: p>
NSManagedObjectContext *child = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[child setParentContext:self.managedObjectContext];
[child performBlock:^{
// Create importer instance, passing it the child MOC...
}];
4 Réponses :
Juste une idée architecturale: p>
Avec votre motif de rafraîchissement de données indiqué (une fois par jour, le cycle complet des données supprimé et ajouté), je serais motivé pour créer un nouveau magasin persistant chaque jour (c'est-à-dire nommé pour la date du calendrier), puis à l'achèvement de l'achèvement. NOTIFICATION, Demandez à la vue Table de configurer une nouvelle fetchedResultController associée au nouveau magasin (et probablement un nouveau MOC) et d'actualiser en utilisant cela. Ensuite, l'application peut (ailleurs, peut-être également déclenchée par cette notification) détruit complètement l'ancien magasin de données. Cette technique découle le traitement de la mise à jour à partir du magasin de données que l'application utilise actuellement et que le "commutateur" sur les nouvelles données peut être considéré comme considérablement plus atomic em>, car le changement arrive simplement commencer à pointer vers Les nouvelles données au lieu d'espérer que vous n'atteignez pas le magasin dans un état incohérent alors que de nouvelles données sont en cours d'écriture (mais ne sont pas encore terminées). P>
Évidemment, j'ai laissé des détails, mais j'ai tendance à penser que beaucoup de données modifiées tout en étant utilisées EM> devraient être réinstallées afin de réduire la probabilité du type de crash que vous connaissez. < / p>
heureux de discuter plus loin ... p>
J'aime cette idée, j'aime bien beaucoup b>. Il présente également l'avantage qu'un système de fichiers supprime le fichier SQLITE sera beaucoup plus rapide que de supprimer les données de base supprimez les objets gérés dans le graphique de l'objet, bien que, bien sûr, la performance de suppression n'a pas d'importance, car un magasin persistant différent sera utilisé quand même. Je vais donner cette approche un coup ce week-end.
Pré-iOS 5, nous avons généralement deux Un aspect important de celui-ci est que le Je ne suis pas sûr de l'avantage d'avoir une relation parent-enfant entre les deux contextes. Il semble de votre description que le disque final sur le disque se produit sur le fil principal, ce qui n'est probablement pas idéal pour des raisons de performance. (Surtout si vous supprimez une grande quantité de données; le coût majeur de la suppression de nos applications s'est toujours produit lors de la sauvegarde finale sur le disque.) P>
Quel code êtes-vous en cours d'exécution lorsque les contrôleurs apparaissent / disparaissent qui pourrait causer des problèmes de données de base? Quels types de traces de pile voyez-vous l'accident sur? P> nsmanagedObjectContextes code>: un pour le fil principal, un pour un fil d'arrière-plan. Le thread de fond peut charger ou supprimer des données, puis sauvegarder. Le
nsmanagedObjectContextDididSacAntification code> a ensuite été passé (comme vous le faites) sur le fil principal. Nous avons appelé
mergechangesfrommanagedObjectContextDidsAnotification: code> pour amener ceux dans le contexte principal du fil. Cela a bien fonctionné pour nous. P>
Enregistrer: code> sur le thread de fond bloque em> jusqu'à après le
MergechangesfrommanagedObjectContextDididsacotification: Code> Finitions exécutées sur le fil principal (Parce que nous appelons des mergechanges ... de l'auditeur à cette notification). Cela garantit que le contexte de l'objet géré le fil principal voit ces changements. Je ne sais pas si vous besoin em> de faire cela si vous avez une relation parent-enfant, mais que vous avez fait dans l'ancien modèle pour éviter divers types de problèmes. P>
Merci. En fait, je n'utilise pas MergechangesfrommaniedObjectContextDididSacotification: Code> Parce que dans l'utilisation normale, les nouvelles données apparaissent parfaitement bien sans elle. Cependant, je l'ai utilisé dans une version antérieure et eu les mêmes accidents lors de la commutation rapidement entre les deux écrans.
Le problème principal que j'ai eu avec des données de base multi-filetés est d'accéder par inadvertance à un objet géré dans un fil / une file d'attente autre que celui créé par celui-ci. P>
J'ai trouvé un bon outil de débogage consiste à ajouter Nsasserts pour vérifier que les objets gérés créés dans votre contexte d'objet géré principal ne sont utilisés que là-bas et ceux créés dans un contexte d'arrière-plan ne sont pas utilisés dans le contexte principal. p>
Cela impliquera la sous-classement NSManèdeObjectContext et NSManageDObject: P>
Ce n'est que quelques lignes de code, mais à long terme peut vous empêcher de faire des erreurs difficiles à suivre autrement. P>
Merci, mais je ne suis pas sûr comment obtenir la file d'attente de la MOC puisqu'elle est instanciée à l'aide de initwithconcurrencyype: NSprivateQueconCurencyType code> Ce qui signifie qu'il crée sa propre file d'attente privée.
Que diriez-vous de Dispatch_Get_Current_Queue à l'intérieur de votre performance:
Cette file d'attente est privée; Essayer d'y accéder directement est quelque chose que les pommes appellent comme une mauvaise idée.
@Jesserusak True, mais je ne vous suggère pas de faire quoi que ce soit, utilisez-le simplement pour comparer la file d'attente que la MOC d'un objet géré a été créée sur / avec.
Ma question initiale est, comment se trouvent la récupération et la recharge de la TableView liées au début de l'opération Supprimer. Y a-t-il une chance que le bloc de suppression sauvegardera l'enfant MOC alors que le est-il possible que lorsque vous passez de la vue de détail à la maîtrise, puis de retour à la vue détaillée, il y aura plusieurs tâches d'arrière-plan simultanées en cours d'exécution? Ou récupérez-vous toutes les données du service Web à une fois non seulement pertinentes pour une rangée particulière? P>
Une alternative pour rendre cela plus robuste consiste à utiliser un motif similaire à ce que Au lieu d'utiliser un parent MOC en tant que Type de fil principal, Donc, si vous avez fait vos suppressions sur une file d'attente parentale qui est privée, cela ne se retrouverait pas dans le Une alternative que j'offre est d'utiliser trois contextes: p>
MOC MOC STRAND> ( enfant MOC A fort> ( NsfetchedResultStroller code> a été prouvé un peu sensible aux suppressions massives, de sorte que je commencerais à creuser en premier. p>
NsfetchedResultSluTroller code> est toujours chercher ou non? P>
uimanagedDocument code> utilise: p>
UimanageDDocument Code> crée en fait la MOC principale en tant que file d'attente privée et rend l'enfant MOC disponible pour vous utiliser sur le fil principal. Le bénéfice ici est que tous les E / S se poursuivent en arrière-plan et sauvent au parent MOC n'interfèrent pas du tout que l'enfant MOC jusqu'à ce que l'enfant MOC soit explicitement fait savoir à leur sujet. C'est parce que sauvegardez des changements d'enfant au parent et non l'inverse. P>
NsfetchedResultSluTroller code> Portée du tout. Et puisqu'il s'agit d'anciennes données, c'est en fait la manière préférée. P>
NSprivateQueconCurencyType Code>) P>
NSMainQuEConCurencyType code>) p>
NsprivateQueconCurencyType Code>, enfant d'enfant MOC A) P>