2009-07-27 5 views
2

J'ai une question liée à la gestion de la mémoire dans une application iPhone multithread. Disons que nous avons cette méthode, qui est appelé dans un thread séparé de la main-UI-fil:L'application iPhone multithread se bloque avec [NSAutoreleasePool release]

- (BOOL)fetchAtIndex:(NSUInteger)index 
{ 
    NSURL *theURL = [NSURL URLWithString:[queryURLs objectAtIndex:index]]; 
    // Pay attention to this line: 
    NSData *theData = [[NetworkHelper fetchFromNetwork:theURL] retain]; 

    // Some code here... 

    // Now what should I do before returning result? 
    //[theData release]; ?? 
    //[theData autorelease]; ?? 
    return YES; 
} 

Comme vous pouvez le voir, je retiens la NSData je suis revenu de mon opération réseau. La question est: pourquoi ne devrais-je pas la libérer (ou la libérer automatiquement) à la fin de ma méthode? La seule façon dont je l'ai fait fonctionner est d'utiliser retain au début, et rien alors. Si j'utilise une autre combinaison (rien du tout: retain puis release ou autorelease), mon programme se bloque avec EXC_BAD_ACCESS lorsque je libère le thread NSAutoreleasePool. Qu'est-ce qui me manque?

Pour votre information, voici le code principal du fil:

- (void)threadedDataFetching; 
{ 
    // Create an autorelease pool for this thread 
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 

    // Reload data in separate thread 
    [self fetchAtIndex:0]; 

    // Signal the main thread that fetching is finished 
    [self performSelectorOnMainThread:@selector(finishedFetchingAll) withObject:nil waitUntilDone:NO]; 

    // Release all objects in the autorelease pool 
    [pool release]; // This line causes EXC_BAD_ACCESS 
} 

Merci pour votre aide!

Répondre

3

Vous ne devez pas libérer des choses que vous ne vous êtes pas retenues (en utilisant retain ou implictly par des méthodes avec: init, new ou copy en leur nom).

Si vous conservez le résultat de fetchFromNetwork, vous devez le libérer. Les deux release et autorelease devraient fonctionner (ne touchez pas l'objet après release, il est plus sûr de définir les champs/variables à nil après release).

Si vous ne conservez pas les données, vous n'avez même pas besoin de les conserver. [NetworkHelper fetchFromNetwork] devrait retourner l'objet autoeleased. Corps de fetchFromNetwork pourrait ressembler à ceci:

NSData *data = [[NSData alloc] init]; 
// stuff happens 
return [data autorelease]; 

ou

NSData *data = [otherObject dataFromOtherObject]; 
// stuff happens 
return data; // don't (auto)release, since you haven't retained 

En cas de doute, pécher par excès qui fuit et exécuter l'application via des instruments "Fuites" ou LLVM checker.

+0

Merci. En effet, fetchFromNetwork était comme votre deuxième exemple de code: NSData * data = [otherObject dataFromOtherObject]; ... return [données autorelease]; // Mauvais ici! C'est pourquoi j'ai dû le conserver dans mon code client. J'ai supprimé le mauvais appel -autorelease, puis le code -retain sur le côté client, et maintenant tout va bien. Merci! – Romain

0

Le fait que vous soyez sur un fil séparé n'est probablement pas pertinent ici. La gestion de la mémoire est la même, et vous devriez équilibrer votre conservation/libération de NSData comme s'il s'agissait du thread principal. Le fait que vous vous écrasiez en vidant le pool autorelease suggère que vous avez probablement fait quelque chose avec theData que vous ne nous avez pas montré ici (ce qui semble évident puisque le code montré n'est pas très utile). Que faites-vous avec les données qui exigent que quelqu'un d'autre le conserve?

+0

Vous avez raison, comme je l'ai écrit dans l'autre commentaire, fetchFromNetwork autoreleasing l'objet NSData donc je devais le conserver dans ce code pour que cela fonctionne, mais la partie buggy était dans la méthode fetchFromNetwork. Merci. – Romain

0

Comme d'autres l'ont noté, il n'est pas vraiment pertinent qu'il soit multithread dans cette instance. Il vaut la peine de lire sur le Objective C Memory Management Guidelines qu'Apple produit.

En gros, tous les objets que vous [[Foo alloc] init] vous-même explicitement, vous devez assumer la responsabilité du nettoyage. Si vous renvoyez cette valeur à partir d'une méthode, vous devez également la libérer.

Tout objet que vous obtenez d'un appelant qui ne commence pas par initWith (tel que [Foo videFoo]) il est de la responsabilité de l'appelant de posséder la ressource (c'est-à-dire de le libérer automatiquement).

Si vous obtenez un objet et que vous souhaitez le conserver en dehors de l'appel de méthode (c.-à-d.enregistrez-le dans un champ d'instance), vous devez le conserver. Lorsque vous avez terminé, vous le libérez, généralement dans le destructeur. Si vous utilisez les propriétés d'Objective C, le définir comme @retain lancera automatiquement ce comportement pour vous; dans le destructeur, vous pouvez simplement définir la propriété à zéro.