2015-04-21 6 views
0

Comme vous pouvez le voir, le code ci-dessous ne fait pas beaucoup (tous les commentaires) plus qu'énumérant sur un ensemble de fichiers, cependant, mon utilisation de la mémoire passe à plus de 2 Go après 40 secondes d'exécution de la fonction ci-dessous qui est lancée en appuyant sur un bouton sur l'interface utilisateur.Fuite de mémoire - Objectif C avec ARC activé

Je peux exécuter l'interface utilisateur pendant des heures, et avant d'appuyer sur le bouton, l'utilisation de la mémoire ne dépasse pas 8 Mo.

Étant donné que l'ARC est activé, que contient la mémoire?

removed original code as the edit below made no differance.

EDIT:

Tentative @autoreleasepool{ dispatch_asyny ... } et permutations de qu'autour du temps et à l'intérieur de la boucle while qui n'a eu aucun effet.

Voici le code avec autorelasepool ajouté et nettoyé

-(void) search{ 

    self.dict = [[NSMutableDictionary alloc] init]; 
    NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:@"/tmp/SeaWall.log"]; 

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
     NSString *bundleRoot = @"/"; 
     NSFileManager *manager = [NSFileManager defaultManager]; 
     NSDirectoryEnumerator *direnum = [manager enumeratorAtPath:bundleRoot]; 
     NSString *filename; 

     while ((filename = [NSString stringWithFormat:@"/%@", [direnum nextObject]]) && !self.exit) { 
      @autoreleasepool { 

       NSString *ext = filename.pathExtension; 

       if ([ext hasSuffix:@"so"] || [ext hasSuffix:@"dylib"]) { 
        if (filename == nil || [NSURL URLWithString:filename] == nil) { 
         continue; 
        } 

        NSData *nsData = [NSData dataWithContentsOfFile:filename]; 
        if (nsData != nil){ 
         NSString *str = [nsData MD5]; 
         nsData = nil; 

         [self writeToLogFile:[NSString stringWithFormat:@"%@ - %@", [filename lastPathComponent], str]]; 

        } 
       } 
       ext = nil; 
      } // end autoreleasepool 
     } 
     [fileHandle closeFile]; 
     [self ControlButtonAction:nil]; 


    }); 
} 

Memory Usage

Répondre

2

La mémoire est pas exactement fuite: il est très prêt à être libéré, mais il n'a jamais une chance d'être .

ARC s'appuie sur les règles de gestion manuelle de la mémoire d'Objective-C. La règle de base est que "l'objet/fonction qui appelle init possède la nouvelle instance", et le propriétaire doit release l'objet lorsqu'il n'en a plus besoin.

Ceci est un problème pour les méthodes pratiques qui créent des objets, comme [NSData dataWithContentsOfFile:]. La règle signifie que la classe NSData possède l'instance, car elle l'a appelée init. Une fois que la valeur sera retournée, la classe n'aura plus besoin de l'objet et elle devra le libérer. Cependant, si cela se produit avant que l'appelé ait une chance de conserver l'instance, il sera parti avant que quelque chose ait eu une chance de se produire. Pour résoudre ce problème, Cocoa introduit la méthode autorelease. Cette méthode transfère la propriété de l'objet au dernier pool de libération automatique qui a été configuré. Les pools Autorelease sont "drainés" lorsque vous quittez leur portée. Cocoa/AppKit/UIKit configure automatiquement les pools de libération automatique autour des gestionnaires d'événements, vous n'avez donc généralement pas à vous soucier de cela. Cependant, si vous avez une méthode à long terme, cela devient un problème.

Vous pouvez déclarer une piscine autorelease utilisant l'instruction @autoreleasepool:

@autoreleasepool 
{ 
    // code here 
} 

Au support de clôture, les objets collectés par le pool autorelease sont libérés (et peut-être désalloué, si personne d'autre n'a une référence à).

Donc, vous devriez envelopper le corps de votre boucle dans cette instruction.

Voici un exemple.Ce code « fuites » environ 10 méga-octets par seconde sur mon ordinateur, parce que l'exécution ne quitte jamais la portée @autoreleasepool:

int main(int argc, const char * argv[]) 
{ 
    @autoreleasepool 
    { 
     while (true) 
     { 
      NSString* path = [NSString stringWithFormat:@"%s", argv[0]]; 
      [NSData dataWithContentsOfFile:path]; 
     } 
    } 
} 

D'autre part, avec cela, le reste utilisation de la mémoire stable, car l'exécution laisse le champ @autoreleasepool à la fin de chaque itération de la boucle:

int main(int argc, const char * argv[]) 
{ 
    while (true) 
    { 
     @autoreleasepool 
     { 
      NSString* path = [NSString stringWithFormat:@"%s", argv[0]]; 
      [NSData dataWithContentsOfFile:path]; 
     } 
    } 
} 

Création d'objets dans la condition de la boucle est gênant pour les boucles longues, car ceux-ci ne sont pas pris en charge par le @autoreleasepool intérieur. Vous devrez également les inclure dans la portée @autoreleasepool.

+0

Je ne sais pas ce que vous entendez par le corps de la boucle. Donc, je l'ai essayé avec ce qui suit: Autour de 'dispatch_async' qui n'a rien fait. à l'intérieur 'dispatch_async' et enfermant tout le reste qui n'a rien fait non plus. Autour de la boucle tout ce qui n'a rien fait non plus. – Cripto

+0

@Cripto il doit être à l'intérieur de la boucle. Le corps de la boucle est le code entre parenthèses après la ligne 'while'. – zneak

+0

S'il vous plaît jeter un oeil à mon édition. Cela ne semble pas fonctionner comme prévu. Merci – Cripto

0

La mémoire doit être libérée par un pool d'autorelease. Sinon, il sera verrouillé pendant que vous rencontrez et il va fuir.

Dans votre boucle mis:

@autoreleasepool { /* BODY */ }