2010-07-28 8 views
0

J'ai une petite application iPhone qui a un bouton sur la première vue. Lorsque je sélectionne ce bouton, je charge ma nouvelle vue qui contient une image. J'utilise actuellement le code suivant pour charger l'image à partir d'une source en ligne sur un thread séparé, tout en permettant à l'utilisateur de continuer à contrôler l'application:NSAutoreleasePool problèmes sur l'iPhone

- (void) loadImageTest 
{ 
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 

    NSURL *url = [[NSURL alloc] init]; 
    url = [NSURL URLWithString:logoPath]; 

    NSData *data = [[NSData alloc] init]; 
    data = [NSData dataWithContentsOfURL:url]; 

    loadingImage = [UIImage imageWithData:data]; 
    titleLogoImage.image = loadingImage; 

    //[pool drain]; 
    [pool release]; 
} 

Ceci est appelé à partir de cette ligne de code dans la nouvelle vue de INIT:

[NSThread detachNewThreadSelector:@selector(loadImageTest) toTarget:self withObject:nil]; 

maintenant, cela fonctionne très bien (ish), mais si je ferme la nouvelle vue, puis charger un nouveau à nouveau en succession rapide (ou juste après les salles, dans certains cas), il bombardera avec le classique EXC_BAD_ACCESS. Je suis sûr que cela est dû au code dans le pool de threads, mais quelqu'un peut-il voir pourquoi cela se produit?

Merci

+0

Les zombies vous sauver. http://www.cocoadev.com/index.pl?NSZombieEnabled –

Répondre

2

vôtre:

// Ceci est ok, vous pouvez essayer d'utiliser des méthodes asynchrones NSURLConnections au lieu de faire // votre propre fil. [NSThread detachNewThreadSelector: @selector (loadImageTest) toTarget: self avecObjet: nil];

- (void)loadImageTest 
{ 
    // This is fine 
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
    // you're making and then abandoning this url object so it will leak 
    NSURL *url = [[NSURL alloc] init]; 
    // this is fine 
    url = [NSURL URLWithString:logoPath]; 
    // again making and abandoning an object 
    NSData *data = [[NSData alloc] init]; 
    data = [NSData dataWithContentsOfURL:url]; 
    // this works, but is not thread safe, 
    // can't operate on UIKit objects off the 
    // main thread 
    loadingImage = [UIImage imageWithData:data]; 
    titleLogoImage.image = loadingImage; 
    // this is fine 
    //[pool drain]; 
    [pool release]; 
} 

dépollués pour rendre les choses thread-safe, etc. devraient aider:

// I'm assuming you have a iVar for logoPath but we don't want to 
// make threaded calls to an iVar (it's not mutable, so you could do it, but it's just bad form) 
// If i'm wrong about logoPath being an iVar don't sweat copying it. 
- (void)whateverMethodYouWant 
{ 
    NSString *aLogoPath = [[logoPath copy] autorelease]; 
    [NSThread detachNewThreadSelector:@selector(loadImageForPath:) toTarget:self withObject:aLogoPath]; 
} 
- (void)loadImageForPath:(NSString *)aLogoPath 
{ 
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 

    NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:aLogoPath]]; 
    // the performSelector will retain the data until the method can be performed 
    [self performSelectorOnMainThread:@selector(setImageForTitleLogo:) withObject:imgData waitUntilDone:NO]; 

    [pool release]; 
} 
- (void)setImageForTitleLogo:(NSData *)imgData 
{ 
    // This part is not strictly necessary 
    // but it's a nice way to wrap a method that needs to happen on the main thread. 
    if ([NSThread isMainThread]) 
    { 
     // make the image (i'm assuming you meant loadingImage to be a local scope variable) 
     UIImage *loadingImage = [UIImage imageWithData:imgData]; 
     // make sure the button still exists 
     // also make sure you're setting any references to this button to nil when you're releasing and making new views 
     // so that you're not addressing a button that's been released 
     // (assigning the image to the button will cause the button to retain it for you) 
     if (titleLogoImage != nil) 
      titleLogoImage.image = loadingImage; 
    } 
    else 
    { 
     [self performSelectorOnMainThread:@selector(setImageForTitleLogo:) withObject:imgData waitUntilDone:NO]; 
    } 
} 
+0

Hey merci pour cela, vous nouveau à SO? –

+0

Excellent de toute façon :) Je pense qu'il ne faisait pas le réglage de l'image sur le fil principal qui était le problème. Merci beaucoup :) –

+0

J'arrive de temps en temps. Content que cela ait aidé. – Doug

0

Vous faites des trucs bizarres.

NSURL *url = [[NSURL alloc] init]; 

signifie que vous créez un objet NSURL, que vous possédez.

url = [NSURL URLWithString:logoPath]; 

Cela signifie que vous créez un autre objet NSURL, qui est autoreleased. Le NSURL que vous venez de créer ne fait que fuir. La même chose vaut pour le NSData ici.

Le loadingImage doit être conservé par titleLogoImage, ou il sera désalloué sur le drain de votre NSAutoReleasePool. Qu'est-ce que titleLogoImage et conserve-t-il image? Ps: ce qui me dérange aussi, c'est que loadingImage ne se limite pas à la portée de la fonction. Pour raccourcir les choses:

NSURL *url = [NSURL URLWithString:logoPath]; 
NSData *data = [NSData dataWithContentsOfURL:url]; 
titleLogoImage.image = [UIImage imageWithData:data]; 

peut sauver au moins quelques maux de tête.

+0

C'est ce que j'avais, mais en lisant sur NSAutoReleasePool, j'ai lu quelque part pour le faire ... Quoi qu'il en soit, même avec votre méthode, que j'avais auparavant, J'ai le même problème. Merci quand même. –

0

Il n'y a rien dans le code affiché qui déclencherait un plantage. Selon comment titleLogoImage est défini, il peut être affecté par l'autorelease.

Cependant, beyond the problems outlined by mvds, vous n'avez aucun besoin pressant pour un pool d'autorelease localisé. Il ne fera rien ici mais vous causer des problèmes.

Les piscines à autorisation automatique sont dangereuses et non pour les débutants. Ils vont tuer tous les objets autoreleased en eux. Vous ne créez généralement votre propre pool que lorsque vous créez rapidement un grand nombre d'objets gourmands en mémoire. Cela ne semble pas être le cas ici.

Malgré l'attention qui leur est accordée, les piscines personnalisées sont rarement utilisées. Après plus d'une décennie de travail Apple API, je peux probablement compter sur mes doigts le nombre de fois que j'ai utilisé mon propre pool personnalisé.

Questions connexes