3

J'ai une application qui s'exécute en arrière-plan uniquement (en spécifiant LSBackgroundOnly dans le fichier info.plist). Le problème est que tous les blocs que je cours sur des files d'attente parallèles ne sont pas libérés. Le code est exécuté dans un environnement géré en mémoire - aucun GC n'est impliqué.Le bloc Objective-C n'est pas libéré pour les applications en arrière-plan

Le code (simplifié) ressemble à ci-dessous. Blubber est juste une classe factice qui contient un NSDate à tester. En outre, elle écrasera retain, release et dealloc faire une exploitation forestière:

NSOperationQueue *concurrentQueue = [[NSOperationQueue alloc] init]; 
[concurrentQueue setMaxConcurrentOperationCount:NSOperationQueueDefaultMaxConcurrentOperationCount]; 

Blubber *aBlubber = [[Blubber alloc] init]; 
aBlubber.aDate = [NSDate date]; 

[concurrentQueue addOperationWithBlock:^{  
NSAutoreleasePool *blockPool = [[NSAutoreleasePool alloc] init]; 
    NSDate *test = [aBlubber aDate]; 
    NSLog(@"Block DONE"); 
    [blockPool release];  
}]; 

[aBlubber release]; 

[concurrentQueue release]; 

Si je change l'application d'une application normale (non-backgound), je peux observer les blocs étant libérés chaque fois que l'entrée est faite via l'interface utilisateur (même en changeant la mise au point à une autre fenêtre est suffisante). Depuis mon application backgorund reçoit une entrée directement sur le pilote USB HID et il n'a pas une fenêtre ou une barre de menu cela ne se produit pas.

Existe-t-il un moyen de forcer manuellement le runloop ou tout ce qui est responsable de dire aux files d'attente de libérer les blocs finis?

(Tous les autres objets qui avaient été retenus par les blocs sont pas libérés, ce qui crée d'énormes fuites de mémoire. Ces fuites ne peuvent pas être spottet par les outils Fuites ou ObjectAllocations, mais la consommation de mémoire peut observer la montée en flèche en utilisant le dessus.)

+0

Avez-vous essayé d'utiliser les fonctions C (libDispatch) au lieu de NSOperations? – NSSplendid

+0

Je pense qu'il donne le même résultat, car NSBlockOperation est documenté comme utilisant libDispatch en interne. Merci pour la suggestion, de toute façon, je vais donner un tourbillon dès que possible. –

Répondre

2

Un "gotcha" commun pour les pools autorelease est que si l'application accumule de la mémoire sans recevoir d'événements, le pool le plus externe (celui géré par la boucle d'événement) ne sera pas drainé.

Je ne pense pas que cela devrait appliquer ici puisque vous gérez votre propre piscine ... mais juste au cas où, vous pouvez essayer ceci:

... 
//When no events are coming in (i.e. the user is away from their computer), the runloop doesn't iterate, and we accumulate autoreleased objects 
[[NSTimer scheduledTimerWithTimeInterval:60.0f target:self selector:@selector(kickRunLoop:) userInfo:nil repeats:YES] retain]; 
... 
- (void) kickRunLoop:(NSTimer *)dummy 
{ 
// Send a fake event to wake the loop up. 
[NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined 
            location:NSMakePoint(0,0) 
           modifierFlags:0 
            timestamp:0 
           windowNumber:0 
            context:NULL 
            subtype:0 
             data1:0 
             data2:0] 
     atStart:NO]; 
} 
0

Il semble que vous utilisez une pile à base bloc qui est utilisé après que le bloc est sorti de la portée. Le bloc doit être copié. Le code devrait fonctionner si elle est modifiée à ceci:

[concurrentQueue addOperationWithBlock:[[^{  
    NSAutoreleasePool *blockPool = [[NSAutoreleasePool alloc] init]; 
    NSDate *test = [aBlubber aDate]; 
    NSLog(@"Block DONE"); 
    [blockPool release];  
}copy]autorelease]]; 

Jetez un oeil à ce poste pour un writeup complet sur des blocs: http://gkoreman.com/blog/2011/02/27/blocks-in-c-and-objective-c/

+0

Nous vous remercions de votre suggestion. Je suis assez sûr, cependant, que le NSBlockOperation qui enveloppe le bloc quand j'utilise addOperationWithBlock prend soin de transférer le bloc au stockage approprié. Mon problème n'est pas que le bloc n'exécute pas ou qu'il se comporte bizarrement; mon problème est que le bloc continue à étreindre la mémoire même s'il a été mis dans le pool autorelease si l'application est sans interface utilisateur. Je suis toujours en train d'expérimenter, mais il semble que @Catfish_Man ait atteint son but avec sa réponse. –