6
votes

Utilisation de la matrice Singleton synchronisée avec Nsthread

J'ai une application de livres avec une barre Uiseearch, où l'utilisateur tape n'importe quel nom de livre et obtient des résultats de recherche (à partir de l'appel de l'API ext) ci-dessous en tant que types.

J'utilise une variable singleton dans mon application appelée RevOUREDARRAY qui stocke Tous les livres. P>

- (void) searchTableView {
    NSString *searchText = searchBar.text;
    NSMutableArray *searchArray = [[NSMutableArray alloc] init];

    for (int i=0;i<[retrievedArray count];i++)
    {
        Stock *aBookTemp = [retrievedArray objectAtIndex:i];
        NSString *temp = [aBookTemp valueForKey:@"BookName"];
        [searchArray addObject:temp];
    }

    for (NSString *sTemp in searchArray)
    {
        NSRange titleResultsRange = [sTemp rangeOfString:searchText options:NSCaseInsensitiveSearch];

        if (titleResultsRange.length > 0)
            [copyListOfItems addObject:sTemp];
    }

    [searchArray release];
    searchArray = nil;
}


0 commentaires

5 Réponses :


2
votes

J'ai initialement suggéré de supprimer le mot-clé nonatomic de votre déclaration de propriété. Atomic est la valeur par défaut (il n'y a pas de paramètre Atomic , omettez nonatomic est suffisant) - qui gérera la sécurité du fil pour vous en enveloppant le set synthétisé dans un @sysynchroniser bloc.

Malheureusement, beaucoup de gens ont appris juste à mettre nonatomic sur tout le code sans vraiment comprendre. J'ai toujours pensé que cela vient de copier / pâte hors code d'échantillon Apple - ils l'utilisent souvent pour des trucs liés à l'interface utilisateur - rappelez-vous que Uikit n'est pas le fil de fil.

Anomie a indiqué dans sa réponse que ce n'est-ce pas - probablement - parce que vous manchonnez une matrice mutable de différents fils. que sonne comme la bonne réponse à moi - je supprimerais ma réponse, mais je vais le laisser ici, comme je pense que mes commentaires valent la peine d'être quelque chose (encore 100% pertinent pour votre problème).


3 commentaires

Merci pour votre réponse ... j'ai supprimé nonatomique du fichier partagé.h (conserver) NsmutableTray * livres; Mais toujours le problème se produit ...


Avoir un getter atomique et un setter signifie que l'obtention et la réglage de la matrice seront atomiques, mais l'accès au contenu de la matrice ne sera toujours pas. Vous avez raison que cela devrait être atomique, mais il doit également utiliser @synchroniser autour du code qui modifie / lit le tableau. Il serait plus simple et plus efficace d'utiliser la solution de @ Anomie d'utiliser un tableau séparé pendant la mise à jour, auquel cas les a atomiques seraient suffisantes.


Si l'objet partagé avait des méthodes @Synchronisées pour manipuler la matrice de livres, l'accès simultané au contenu de la matrice serait résolu.



5
votes

De ce que vous avez posté, chaque récupérateur de récupération pointe sur le même objet NsmutableRayRay. Donc, il n'y a pas de tableaux séparés pour rester synchronisé, c'est tout le même tableau.

Cependant, NsmutableArray n'est pas un fil de sécurité; Les choses peuvent faire exploser si un fil le change alors qu'un autre le lit. Il suffit de changer la propriété de nonatomic en atomique est insuffisante, car cela ne couvre que la récupération de l'objet Array lui-même et non des appels de méthode ultérieurs d'accès à l'intérieur du tableau. Je ne pense pas que cela entraîne votre problème principal, cependant, et que le correctif pour cela devrait éviter le problème de sécurité du fil.

Je suppose que la séquence d'événements est quelque chose comme ceci:

  1. La vue de la liste affiche un ensemble de résultats qui inclut A à index n.
  2. L'utilisateur tape quelque chose. L'analyseur XML commence à mettre à jour progressivement le tableau partagé. La vue de liste n'est pas encore mise à jour.
  3. L'utilisateur touche l'élément à l'index n dans la vue de la liste. La vue de la liste indique la vue détaillée pour afficher l'élément à l'index n.
  4. La vue de détail extrait l'élément de l'index N à partir du tableau partagé, mais en raison de la mise à jour démarrée à l'étape 2 Index N maintenant contient B. Quelle vue de détail s'affiche.
  5. à un moment donné, l'analyse XML complète et maintenant est mise à jour.

    Il devrait également être possible, si la charge et l'analyse du service Web sont suffisamment lentes, pour l'étape 4 pour simplement planter avec une norme NsgrangeException.

    Une solution serait pour chaque élément de la liste pour contenir l'objet de résultat réel et transmettre cela à la vue détaillée plutôt que simplement l'index. Vous pourriez être en mesure de vous débarrasser complètement de la matrice partagée dans ce cas, si la liste et les détails sont les seuls consommateurs ou si d'autres consommateurs peuvent être modifiés pour prendre des objets au lieu d'indices de la même manière. Un autre serait pour l'analyseur d'accumuler les résultats dans une matrice privée et de mettre à jour le tableau partagé tout à la fois juste avant de signaler la vue de liste pour se mettre à jour; Il reste encore une légère possibilité pour une course dans le temps entre la mise à jour sur le fil d'arrière-plan et l'invocation de la méthode sur le fil principal, mais la fenêtre est probablement un peu plus petite.

    Ou je pourrais être complètement faux dans ma supposition sur la façon dont la mise à jour fonctionne, auquel cas vous devriez fournir plus de détails.


24 commentaires

Hey Anomie ... C'était très difficile pour moi d'expliquer la question ici. Mais vous semblez avoir compris le problème très précisément ... maintenant venir à la solution, je voudrais aller à la 2e approche suggérée par vous. "Un autre serait pour l'analyseur d'accumuler les résultats dans une matrice privée et de mettre à jour le tableau partagé tout à la fois juste avant de signaler la vue de liste pour se mettre à jour" Serait-il possible pour vous de fournir un pseudo code de ce que vous essayez dire. Je peux mettre en œuvre la même chose dans mon application et voir si cela fonctionne.


Mais oui, comme je l'ai dit, car la question ne se produit que lorsque les types d'utilisateurs sont très rapides, il semble avoir quelque chose à voir avec le temps pris pour mettre à jour le tableau dans 2 places. Merci encore pour toute votre aide à ce sujet. J'ai donné de mon mieux pour résoudre le problème sans aucune chance et je suis désespérément vraiment désespérément à résoudre le problème.


Dans vos méthodes NSXMLParserDelegate, vous devez ajouter des objets dans la récupération. Au lieu de cela, ajoutez un champ temporaire à côté du champ de récupération, définissez-le sur une nouvelle nsmutableArraRay juste avant d'appeler [parser parse] , ajoutez les résultats à temporaire dans les méthodes de délégation et Ensuite, après [Parser Parse] Renvoie l'appel [RETROUDEDARRAY RETPLETOBEWJECTSINRANGE: NSMAKAKERANGE (0, RETRAINARAY.COUNT) WithObjectsFromARRAY: TEMPORALARERAY] .


Ou si vous allez à un peu de travail supplémentaire pour tout signaler pour recharger la récupération de la récupération de [[[partage partagé SharedManager] Books] (ou simplement l'accès directement au lieu de copier le pointeur sur une nouvelle place), vous Pourrait simplement utiliser [[[partage SharedManager] Enregistrements: temporaireArray] .


Salut Anomie..Merci de votre aide..Je essayez de comprendre l'approche donnée par vous et de mettre en œuvre la même chose dans mon application..Je suis un peu peu claire avec la 2e approche (signalez tout pour recharger la récupération de récupération). Pourriez-vous s'il vous plaît élaborer un peu à ce sujet ... Je reviendrai aussi avec mes conclusions pour la 1ère approche dès que je peux. Merci encore.


Salut Anomie ... Je viens d'essayer d'implémenter la 1ère approche fournie par vous. Je n'ai édité que la classe XMLParser (c'est-à-dire remplacée de récupération de récupération avec temporaire dans les méthodes de déléguée, une fois que l'analyse est terminée, j'ai utilisé [RETROUDEDARRAY RETPLEDAREDAYRANGE: NSMAKAREAY.COUNT) WithObjectsFromARray: TEMALARRAY]; Les valeurs sont remplies dans TEMORALADRAY ..Avoir que les problèmes de synchronisation se produisent toujours entre la liste et la vue détaillée. IE Si je clique sur l'élément1, les détails figurent pour l'élément3. Veuillez suggérer.


Élaboration de la deuxième approche: Si vous utilisez [[[[[[[[[[[[[


sur la deuxième approche: Si vous utilisez [[[[[[[[[[[[[[
] Temporarray] , qui remplacera complètement le tableau que vous recevez de [[[[partage partage partage] livres] . Mais tout ce qui a déjà fait cela et enregistré le résultat à la récupération de la récupération ne remarquera pas le changement, ils devraient faire Retrouvezparray = [[[[[partage SharedManager] Books]; à nouveau pour le mettre à jour. Ou vous pouvez vous débarrasser de la récupération en tant que ivar complètement et attribuez une variable locale directement à partir de [[[partage partagé SharedManager] Books] en haut de chaque méthode qui a besoin de la liste des livres.


Hmm ... Peut-être que vous devriez éditer la question pour inclure le gestionnaire de clic sur l'élément1.


ok..Je édité la question d'inclure l'exemple1,2,3 exemple ... Je vais essayer également de comprendre la 2e approche et de voir si cela fonctionne dans mon application..Je jet vas-je une fois que j'ai le résultats.


Je voulais dire que vous incluez le code réel qui est exécuté en cliquant sur l'un des éléments.


OK..Je a édité ma question pour inclure le code de sélection. Mais toujours incapable de réparer le problème ...


S'il vous plaît laissez-moi savoir au cas où je devrais fournir des informations supplémentaires.


Anomie: Avez-vous eu la chance de passer par le code mis à jour que j'ai ajouté?


Je manque d'idées. Qu'est-ce que l'analyseur XML a-t-il ressemblé maintenant après toutes les modifications ci-dessus, qu'est-ce que parseDidComplete_srch est sur le contrôleur de vue de la liste, qu'est-ce que tableview: cellsfrotorTindexpath: ressemble, et qu'est-ce que quel que soit le code dans BookdetailCustom qui charge réellement les données ressemblent?


J'ai modifié ma question pour inclure le code de SearchTableView aussi ... PARSEDIDCOMPLETE_SRCH utilise SearchTableView CellfroworTIndexPath utilise COPYLISTems pour afficher l'objet IndexPath.row dans BookDetailCustom, il utilise l'index de la récupération pour afficher la vue détaillée. Cette information est transmise à partir de la vue de la liste.


Pensez-vous qu'un tableau de copie presque en double «Copylistofitems» peut causer le problème. ?


Je pense que je vois ton problème maintenant. Les lignes de la liste de liste correspondent aux index dans COPYLISTODEMMS , tandis que la vue de détail regarde dans Retrouvezparray . Ces tableaux n'ont pas le même contenu! Vous pouvez faire quelque chose comme bookdetailcustom.selectedex = [RETROUDEDARRAY indexObject: [COPYLISTOFITEMS ObjectForindex: IndexPath.row]]]; ou vous pouvez simplement changer bookdetailcustom pour emprunter l'objet au lieu du chemin d'index.


Je viens d'essayer bookdetailcustom.selectedex = [Récupéraparray indexOfObject: [COPYLISTOFITEMS ObjectForindex: IndustryPath.row]]]; Mais il donne une erreur pour ObjectForindex: (non reconnaître / hors limites) Il affiche également des éléments dupliqués dans la vue de liste si je tape très rapidement dans la barre de recherche. Je ne sais pas quelle est la raison ... de toute façon, je suis presque sûr que je suis presque sûr que le problème est dû à ces 2 tableaux Copylistofitems et de récupération ... Voulez-vous que je ouvre une nouvelle question où je ferai en surbrillance la chose de 2 matrices spécifiquement ?


Bah, cela n'aurait pas travaillé de toute façon, j'ai négligé le fait que retirrevedarray contient des objets tandis que Copylistofitems ne contient que la chaîne de nom de livre. Comme je l'ai dit plus tôt, la meilleure chose à faire serait probablement de disposer de chaque cellule de table à l'objet d'actions réelle et d'avoir BookdetailCustomCustom Prendre un objet de stock au lieu d'un index.


ok..J'ai mis à jour le code dans la liste de la liste sur stockdetailcustom.abook = [récupéraire ObjectAindex: indénitation.row]; et en détail vue sur nsstring * tempoffname = abok.officialname; Il affiche le même nom en détail que l'élément de liste cliquée, mais je reçois l'erreur suivante après un certain temps; [NsmutableArray ObjectAindex:]: Index 20 au-delà des limites [0 .. 19] 'Je ne sais pas si cela est lié à cela ... mais juste pour ajouter, ma liste de liste indique également des éléments en double. .


Donc, résumer, je veux que les choses suivantes se produisent; 1. Seulement indiquer environ 10 éléments uniques dans la liste de la liste 2. Afficher l'article cliqué en détail Vue 3. N'affichez pas de duplicats (l'API ne renvoie aucun duplicate..it, c'est que le tableau ne semble pas être effacé. Même si j'ai utilisé RemoveAllobjects) 4. Cela devrait fonctionner même si les types d'utilisateurs sont très rapides.


Lorsque vous modifiez la liste, appelez-vous reloaddata sur la vue Table de la table? Si vous ne le faites pas, il est possible que cela puisse causer à la fois les doublons et l'éventuelle exception. Sinon, dans le menu Exécuter en Xcode, il est "Arrêt sur les exceptions de l'objectif-C"; Activez cela et il s'arrêtera lorsque cette exception se produit, vous pouvez ensuite utiliser les commandes de débogueur normales pour déterminer exactement où dans votre programme l'accès hors limites se produit.


Oui, j'appelle Reloaderdata sur ma table. Donc d'autres, le statut est 1) la liste ne recharge pas rapidement si les types d'utilisateurs sont rapidement dans la barre de recherche. 20 Toutefois, la synchronisation de la liste / détail est appropriée à I.e. Si ABC est cliqué sur la vue de la liste, il affichera le détail d'ABC en détail. (Même si l'utilisateur pourrait avoir rapidement tapé ABD, qui ne s'est pas rafraîchi rapidement / correctement), je vais accepter la réponse pour l'instant et ouvrir une nouvelle question décrivant le statut ci-dessus.


Merci pour tous vos efforts sur celui-ci ... j'en ouvrirais un nouveau dans un certain temps.



0
votes

Essayez d'utiliser un NSRecursivock dans les accessoires de la matrice.

Voir le Nsrecursivockock documentation. À partir de la vue d'ensemble:

NsRecursivockage définit un verrou qui peut être acquis plusieurs fois par le même fil sans causer une impasse, une situation où un thread est bloqué de manière permanente en attente de renoncer à une serrure. Bien que le fil de verrouillage ait une ou plusieurs serrures, tous les autres threads sont empêchés d'accéder au code protégé par le verrou.

Le code d'échantillon COREVIDEO a des exemples de son utilisation appropriée.


2 commentaires

Je n'ai jamais entendu ni utilisé Nsrecursivockock avant. Pourriez-vous s'il vous plaît élaborer plus sur la manière de mettre en œuvre la même chose dans mon application.


Pourquoi la serrure récursive? Un verrou normal ne devrait-il pas être suffisant?



0
votes

Le problème est que récupéré code> est référencé par deux threads. Supprimer toutes les références à RetrouvezRayraray code> à partir de votre code d'analyse XML et ne le modifiez que sur le fil principal.

Voici le processus: P>

  1. Changement PARSEXMLFILE: CODE> Pour créer un nouveau tableau: parseDARRAY = [NSMUBABLARRARAAYRAY] code> li>
  2. Changement Parser: DidendElement: Code> Pour ajouter à ce nouveau tableau: [PARSEDARRAY AddObject: ABOOK] CODE> LI>
  3. in analyseur: didenddocument: code> Passez votre nouveau tableau vers le fil principal: p>

    - (void) updateRetrievedArray: (NSArray *)parsedArray {
        [retrievedArray setArray:parsedArray];
        [self parseDidComplete_srch_book]; // Be sure to call [tableView reloadData]
    }
    
  4. updaterarodearray: code> L'exécution sur le thread principal serait le code responsable de la mise à jour Retrouvezparray code> - de cette façon un seul thread change cet objet: p>

    [delegate performSelectorOnMainThread: @selector(updateRetrievedArray:)
                               withObject: parsedArray
                            waitUntilDone: NO];
    


6 commentaires

La récupération est une matrice partagée et est mise à jour comme suit dans XMLParser. Tout d'abord, tous les objets sont supprimés chaque fois que l'analyse est terminée; - (NOID) PARSEXMLFILE: (NSURL *) URL {[RemoveAraray RemoveAllObjects]; ....} alors dans l'analyseur: Didendelement (F ([ElementName Isequaltostring: @ "BookDetails"]) [Récupéraparray AddObject: Abook];} S'il vous plaît faites le moi savoir au cas où vous auriez besoin de détails supplémentaires.


J'ai mis à jour ma réponse en fonction de votre commentaire. Essayez-moi d'essayer de me faire savoir si cela ne résout pas votre problème.


Oui sûr..Je vais essayer de mettre en œuvre la même chose dans mon application. Je reviendrai avec les conclusions.


Juste 1 question rapide ... pour l'étape 3 ... Parser: DiskIatDocument: Voulez-vous dire parser: Didendelement: S'il vous plaît confirmer


J'ai essayé le code ci-dessus. Mais pour des raisons pour des raisons, cela ne fonctionne pas bien. Je suis incapable d'analyser correctement.


Non, # 3 devrait être lorsque vous avez fini d'analyser le document - si c'est lorsque vous avez mis à jour la table. Chaque fois que vous analysez un élément, vous l'ajoutez à votre parseDarray. Lorsque vous avez terminé l'analyse du document, vous envoyez le parseDarray au fil principal pour mettre à jour la récupération. Mais si votre code n'est pas correctement analysé, c'est un problème distinct tout à fait ...



1
votes

Je comprends que vous avez investi beaucoup de temps et d'efforts dans la correction de cette solution et que la solution d'Anomie est optimale pour cela. Mais peut-être qu'un type d'approche différent pourrait être plus facile à mettre en œuvre.

Par exemple, vous pouvez utiliser le processus d'analyse des données et l'alimenter à un magasin de données de base. La liste serait alimentée par une NsfetchedResultController. Le contrôleur s'occupe automatiquement du contenu de la table et de toute synchronisation à effectuer.

Cela vaut la peine d'essayer et j'espère que cela aide.


0 commentaires