Push, Pop, Push, Pop, Memory Leak

TounToun Membre

Bonjour !

 

J'ai un petit soucis qui me prend la tête, j'aimerai que vous me donniez votre avis.

Voilà  je fais une application pour iOS et j'ai des fuites de mémoire sévère au point que le système tue mon app, elle consomme parfois jusqu'a 550mo ..  :'(  

 

La structure est toute simple :

Une UITableView et lorsqu'on touche une cellule, on push un ViewController. Classique.

Mais si on revient a la vue de la table, qu'on sélectionne une autre cellule (ou la même), rebelote, un ViewController affiche les données.

Le problème c'est que si on fait ça 20, 30 ou 50 fois, a chaque retour a la vue principale, on ne libère pas totalement la mémoire des viewcontrollers.

 

Alors j'ai lu que c'est fait de manière a ce que si on choisi de revenir dessus, il garde en mémoire pour aller plus vite. Mais pour moi c'est un gros problème.

Il s'agit d'une app qui liste des annonces, l'utilisateur peut être amené a ouvrir réellement beaucoup d'annonces.

 

Voici le code de mon didSelectRowAtIndexPath qui push le viewcontroller.

 



- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if(cell.tag != 1) {

[tableView deselectRowAtIndexPath:indexPath animated:YES];

Annonce_ViewController *annonce_viewController = [[Annonce_ViewController alloc] init];
annonce_viewController.annonce = [annonces objectAtIndex:indexPath.row];
[self.navigationController pushViewController:annonce_viewController animated:YES];


}
}

Je sèche. ???


 


Merci de votre aide.


Mots clés:

Réponses

  • LarmeLarme Membre

    Regarde si tu n'as pas un strong qui gênerait la déalloc, ou un NSTimer par exemple.


  • CéroceCéroce Membre, Modérateur
    juin 2016 modifié #3


    Le problème c'est que si on fait ça 20, 30 ou 50 fois, a chaque retour a la vue principale, on ne libère pas totalement la mémoire des viewcontrollers.




    Le premier réflexe à  avoir, dans un tel cas, est d'utiliser Instruments > Leaks (dans le menu Product > Profile de Xcode).


     


    Le code de ton exemple est-il complet ? En particulier, n'y a-t-il pas de délégation avec Annonce_ViewController ?


  • TounToun Membre

    Dans mon didSelectRowAtIndexPath c'est bien le code exacte.


    J'essaye depuis quelque temps d'utiliser Instruments mais c'est une usine a gaz pour moi. Cependant si j'ai bien lu ce qu'il me disait, il disait qu'un élément WebCore était retenu.


     


    Le problème serait donc sur le viewcontroller qui est "pushé". En commentant toutes les méthodes j'ai fini par trouver ce qui cloche.


     


    J'ai une MKMapView qui affiche la ville ou l'annonce se situe.


    Quand j'enlève cette map, je n'ai plus de fuite de mémoire.


     


    Voici le code au complet.



    - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
    if(section == 0) {
    mapView = [[MKMapView alloc] initWithFrame:CGRectMake(0, 0, WIDTH, 110)];
    mapView.zoomEnabled = NO;
    mapView.showsUserLocation = NO;
    mapView.scrollEnabled = NO;

    // Event Touch
    UITapGestureRecognizer *tapOnMap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapOnMap)];
    [mapView addGestureRecognizer:tapOnMap];

    // Line Border
    CALayer *line = [CALayer new];
    line.frame = CGRectMake(0, 109.5f, WIDTH, .5);
    line.backgroundColor = UIColorFromRGB(0x8e8e8e, .5).CGColor;
    [mapView.layer addSublayer:line];

    if([Utilities connected]) {
    // NSString *urlGetLocationCity = [NSString stringWithFormat:
    // @https:;//maps.google.com/maps/api/geocode/json?address=%@ %@&;sensor=false", [self.annonce objectForKey:@ville], [self.annonce objectForKey:@cp]];
    //
    // urlGetLocationCity = [urlGetLocationCity stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLQueryAllowedCharacterSet];

    // [Utilities placeGetRequest:urlGetLocationCity withHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    // if(data != nil) {
    // NSDictionary *dictResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
    //
    // if([[dictResponse objectForKey:@status] isEqualToString:@OK]) {
    // lat = [[[[[[dictResponse objectForKey:@results] objectAtIndex:0] objectForKey:@geometry] objectForKey:@location] objectForKey:@lat] floatValue];
    //
    // lng = [[[[[[dictResponse objectForKey:@results] objectAtIndex:0] objectForKey:@geometry] objectForKey:@location] objectForKey:@lng] floatValue];
    //
    //
    // dispatch_async(dispatch_get_main_queue(), ^{
    // MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(CLLocationCoordinate2DMake(lat, lng), 5000, 5000);
    // [mapView setRegion:[mapView regionThatFits:region] animated:NO];
    //
    // // Add an annotation
    // MKPointAnnotation *point = [[MKPointAnnotation alloc] init];
    // point.coordinate = CLLocationCoordinate2DMake(lat, lng);
    // //point.title = @"";
    // //point.subtitle = @"";
    //
    // [mapView addAnnotation:point];
    // });
    // }
    // else {
    // NSLog(@FAIL search);
    // }
    // }
    // else {
    // NSLog(@FAIL : %@", error);
    // }
    // }];
    }

    return mapView;
    }
    else
    return [UIView new];
    }

    Oui je sais c'est particulier de se servir de "viewForFooterInSection" pour ajouter la vue MKMapView, d'ailleurs c'est peut-être de là  que viens le soucis ?


    En voyant que le problème venait de MKMapView j'ai mis dans un "viewWillDisappear" ceci :



    mapView.delegate = nil;
    [mapView removeFromSuperview];
    mapView = nil;

    Mais c'est pareil, fuite de mémoire.


     


  • CéroceCéroce Membre, Modérateur
    juin 2016 modifié #5

    Je ne vois rien d'évident dans ce code.


     


    Pour Instruments, je suis d'accord que ce n'est pas simple, mais il y a de bons tutos:


    https://developer.apple.com/library/ios/documentation/DeveloperTools/Conceptual/InstrumentsUserGuide/FindingLeakedMemory.html


     


    Ce que tu cherches à  savoir:


    1) quel est l'objet qui est alloué à  chaque push et jamais libéré ("persistent")


    2) quel objet a instancié ou retenu cet objet sans le libérer


    3) voir si lui-même n'est pas retenu par un objet qui ne le retient pas.


     


    Certes, il faut comprendre un peu comment marche Instruments, mais ensuite, c'est un bug qu'on règle en 5 mn.


  • Te serait-il possible de garder la même mapView pour toutes tes cells ?


    Et de ne changer que ses paramètres ?


  • TounToun Membre

    En effet, en mettant par exemple le mapView dans mon Delegate, en l'appelant ensuite, juste en modifiant ses paramètre sa fonctionne.


    La mapView n'est jamais libérée mais sa fonctionne. Merci beaucoup pour votre aide à  tous !  o:)  


  • FKDEVFKDEV Membre

    Si tu as un peu de temps, ce serait encore mieux de trouver la fuite.


     


    Si tu n'as plus de leaks avec le code que tu as montré c'est que cela vient du block.


    Essaye d'enlever juste le dispatch_async qui référence mapview dans ton block.

  • Joanna CarterJoanna Carter Membre, Modérateur
    Tu n'as pas mis le mapView en weak dans le block.


    mapView est plutôt self.mapView. Du coup, il faut le passer dans le block comme weak; sinon, tu crées une référence circulaire entre le block et self.


    e.g.

            __weak typeof(self) weakSelf = self;
            
            [_targetTextField.presentingViewController dismissViewControllerAnimated:YES completion:^
             {
               typeof(weakSelf) strongSelf = weakSelf;
               
               _targetTextField = textField;
               
               UIViewController<AFMKeypadViewController> *keypadViewController = strongSelf.currentKeypadController;
               
               [strongSelf presentKeypadFromTargetTextField:keypadViewController];
             }];
  • Le block retient self, mais qui retient le block ?


  • Joanna CarterJoanna Carter Membre, Modérateur
    self
  • TounToun Membre

    Du nouveau ducoup. J'ai oublié de précisé un point important. Je testé mes codes sous iOS 10 et Xcode 8, avec lequel j'avais tant de leak. Avec Xcode 7 et iOS 9 tout fonctionne et je n'ai pas de fuite de mémoire, l'app ne monte plus a 500mo.


    :D


Connectez-vous ou Inscrivez-vous pour répondre.