2009-12-08 5 views
2

Mon application de visualisation de données génère un pic de consommation de mémoire lors du redessin (setNeedsDisplay qui déclenche drawRect). Je suis actuellement en train de redessiner l'ensemble de la vue qui contient le tracé de données. Cette vue est beaucoup plus grande que l'affichage de l'appareil. Est-il possible de dire à CoreGraphics d'allouer juste assez de mémoire pour dessiner chaque élément (chaque élément est un petit bloc rectangulaire beaucoup plus petit que l'affichage de l'appareil) et de libérer la mémoire quand c'est fini, plutôt que mon approche naïve actuelle?iPhone + UIView. Consommation de mémoire énorme pendant drawRect. Des stratégies pour réduire cela?

Merci d'avance.

-Doug

MISE À JOUR 8 décembre 08h28 EST

Voici le code correspondant avec wordage explicatif. J'utilise Instruments avec ObjectAlloc, Memory Monitor et Leaks en cours d'exécution. La seule fuite de mémoire que j'ai est dû à la NSOperationQueue ne libérant pas de mems. Ceci est mineur et non pertinent.

Architecturalement l'application se compose d'un tableView avec une liste d'endroits intéressants le long du génome humain à inspecter. Lorsqu'une ligne de table est sélectionnée, je mets en file d'attente une opération de collecte de données qui renvoie des données appelées alignmentData. Ces données sont ensuite représentées sous forme de dalles rectangulaires horizontales.

Dans un premier temps, lorsque le tableView lance mon empreinte mémoire est de 5 Mo.

- (void)viewWillAppear:(BOOL)animated { 

    // Initial dimensions for the alignment view are set here. These 
    // dimensions were roughed out in IB. 

    frame      = self.alignmentView.frame; 
    frame.origin.x    = 0.0; 
    frame.origin.y    = 0.0; 
    frame.size.width   = self.scrollView.contentSize.width;  
    frame.size.height   = 2.0 * (self.containerView.frame.size.height); 

} 

Remarque: Après viewWillAppear: est appelé l'empreinte mémoire n'a pas bougé. Même si le alignmentView est dimensionné bien au-delà des dimensions de l'affichage.

Cette méthode est appelée à partir de l'opération de collecte de données.

- (void)didFinishRetrievingAlignmentData:(NSDictionary *)results { 

     // Data retrieved from the data server via the data gathering operation 
    NSMutableData *alignmentData = [[results objectForKey:@"alignmentData"] retain]; 

    NSMutableArray *alignments = [[NSMutableArray alloc] init]; 
    while (offset < [alignmentData length]) { 

     // ... 
     // Ingest alignmentData in alignments array 
     // ... 

    } // while (offset < [alignmentData length])  
    [alignmentData release]; 

    // Take the array of alignment objects and position them in screen space 
     // so that they pack densely creating horizontal rows of alignment objects 
     // in the process. 
    self.alignmentView.packedAlignmentRows = 
    [Alignment packAlignments:alignments basepairStart:self.startBasepairValue basepairEnd:self.endBasepairValue]; 
    [alignments release]; 

    [self.alignmentView setNeedsDisplay]; 

} 

Après cette ligne de code:

self.alignmentView.packedAlignmentRows = ... 

L'empreinte mémoire est de 13,8 Mo

Après cette ligne de code:

[self.alignmentView setNeedsDisplay]; 

Les pointes de l'empreinte mémoire à 21,5 MB , reste là pendant quelques secondes puis revient au niveau préexistant de 13.8 MB

La solution que je cherche me permettrait essentiellement de créer une fenêtre de tampon de rendu horizontal qui est la hauteur d'une seule ligne d'objets d'alignement. Je voudrais y allouer son rendu de mémoire, puis le rejeter. Je le ferais encore et encore pour chaque rangée de données d'alignement.

En théorie, je pourrais rendre une quantité infinie de données avec cette approche qui bien sûr serait excellent ;-).

-Doug

Répondre

6

Voici la - réponse pas si évidente à mon problème de mémoire. Je vais me donner celui-ci parce que je l'ai appris sur le formulaire de forum de développement Apple Rincewind - un ingénieur Apple très utile BTW.

Il se trouve que par tranchage une grande vue en N morceaux plus petits et le rendu dans chacun à son tour, j'encourra une pointe de mémoire qui est à peu près 1/N la taille de la grande vue. Donc, pour chaque plus petite vue: alloc/init, alimente une partie de mes données, setNeedsDisplay. Rincez/répétez pour toutes les N petites vues.

Simple, hein?

Avant d'apprendre cela, j'avais pensé à tort que setNeedsDisplay: myRect l'a fait pour la vue large. Apparemment non.

Merci pour toutes les suggestions gang.

Cheers, Doug @dugla

+0

En découpant vous voulez dire le diviser en petites sous-vues? – skyline75489

1

Ceci est très possible, vous devez spécifier quelles sections de l'écran doivent tirer, vous devez appeler setNeedsDisplayInRect comme décrit here et passer dans un CGRect qui est la zone que vous souhaitez être redessiné.

Ceci est beaucoup, beaucoup plus rapide que de re-dessiner l'écran entier, j'ai eu des problèmes avec cela dans une application de dessin iPhone, j'ai créé il y a un an et demi.

+0

essayé. Pas de joie Jay. Voici où je suis. Remarque, je fais tout cela sous l'œil vigilant d'Instruments. Je taille ma vue dans la méthode viewWillAppear en définissant le cadre. Je frappe ensuite mon serveur de données, saisis les données, le cuisine un peu. remettez-le à ma vue et appelez setNeedsDisplay. Boom! Un hit de 8 à 10 mégaoctets.Ensuite, de façon intéressante, il retourne à l'empreinte mémoire qui a précédé l'appel à setNeedsDisplay. J'ai essayé ceci: [self.alignmentView setNeedsDisplayInRect: CGRectMake (0, 0, 24, 24)] que je pensais être insignifiant. NON! Le même pic de 8 à 10 mégaoctets. Je suis complètement perplexe. – dugla

+0

Désolé dugla, ça fait plus d'un an depuis ma dernière application iPhone commerciale et je n'ai plus les outils pour tester ici. Je suggère qu'il est possible que ce soit simplement une fonction iPhone back end causant ce pic de mémoire. Essayez peut-être de créer un projet de test et appelez un petit écran de rafraîchissement ici. Sur une autre note, faites-vous beaucoup dans votre méthode drawRect? Ce n'est peut-être pas l'appel réel, mais un appel résultant qui provoque cette pointe de mémoire? – Jay

+0

Jay, le mems spike arrive même si je reviens immédiatement de drawRect. Pas de dessin du tout. Remarque: le pic de mémoire ne s'est pas produit lorsque j'ai dimensionné la vue (en définissant ses dimensions). Donc, même si rien ne se passe dans le corps de dessin, je reçois les pointes de mems. Soupir ... – dugla

3

"Cette vue est beaucoup plus grande que l'affichage de l'appareil."

Vous faites défiler la représentation des données en déplaçant la vue? Vous pouvez envisager d'afficher la même taille que l'affichage et d'utiliser CGTranslate pour ajuster le décalage de dessin dans votre fonction drawRect. On dirait que vous dessinez des tonnes de choses, et CoreGraphics ne peut pas dire ce qui est visible et ce qui ne l'est pas.

Vous obtiendrez de meilleures performances de dessin si vous réduisez la taille de l'affichage et insérez des contrôles pour éviter de dessiner des éléments qui sont en dehors des limites de la vue.

+2

+1 J'étais sur le point de suggérer la même chose. –

+0

En fait, le dessin est incroyablement rapide. Je pensais que je devrais faire des tests de limites. Pas si sûr maintenant. Ce n'est pas mon problème (du moins pas pour l'instant). C'est tout sur les mems. Ça me tue. – dugla

+0

Pourriez-vous poster un échantillon représentatif de votre code? Il semble que vous allouiez des tonnes d'objets c objectifs temporaires. Si vous postez un peu de code, nous pourrions être en mesure de le signaler :-) –

0

Outre la suggestion de Ben:

Si vous défiler autour de vos données, pensez à ajouter quelques petites vues sur la scrollview. De cette façon, vous n'avez pas besoin de redessiner la plupart du temps, mais seulement quand une partie de votre scrollview n'est plus couverte. Fondamentalement, si l'un de vos sous-affichages défile complètement hors de vue, déplacez-le vers le côté opposé de la zone visible et redessinez-le en conséquence.

Dans mon application, je défile uniquement horizontalement et j'utilise deux sous-vues. Disons que view1 est à gauche et view2 à droite. Lorsque view2 défile hors de vue, je le déplace vers la gauche de view1 et le redessine en conséquence. Si l'utilisateur continue à défiler dans le même sens, view1 défilera également hors de vue et je le déplacerai à gauche de view2 et ainsi de suite.

Si vous devez faire défiler horizontalement et verticalement, vous aurez besoin de 4 vues.

+0

J'ai réellement une solution hybride. J'ai un UIScrollView qui a un containerView contraint au défilement horizontal via la surcharge de setTransform. Actuellement, il s'agit d'une seule vue à grande échelle qui sera éventuellement un "scroller infini" que j'ai codé mais pas encore incorporé dans mon app viz. Le containerView a une vue enfant qui est défilable verticalement (pas un UIScrollView, juste mon propre scroller simple). – dugla

0

Je sais que vous êtes probablement au courant de cela, mais avez-vous regardé le Core Plot framework pour votre visualisation de données? Nous avons récemment ajouté le défilement tactile des graphiques et nous avons essayé d'être conservateur quand il s'agit de mémoire dans le cadre. Sans en savoir plus sur votre cas spécifique, cela pourrait être quelque chose que vous pourriez essayer.

Questions connexes