2010-01-26 5 views
14

J'ai une fonction qui prend des données bitmap et renvoie un UIImage *. Il ressemble à quelque chose comme:Quel est le bon modèle de gestion de mémoire pour buffer-> CGImageRef-> UIImage?

UIImage * makeAnImage() 
{ 
    unsigned char * pixels = malloc(...); 
    // ... 
    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, pixels, pixelBufferSize, NULL); 
    CGImageRef imageRef = CGImageCreate(..., provider, ...); 
    UIImage * image = [[UIImage alloc] initWithCGImage:imageRef]; 
    return [image autorelease]; 
} 

Quelqu'un peut-il expliquer exactement qui possède quelle mémoire ici? Je veux nettoyer correctement, mais je ne suis pas sûr de savoir comment le faire en toute sécurité. Les documents sont flous sur ceux-ci. Si je free pixels à la fin de cette fonction après avoir créé l'UIImage, puis utiliser le UIImage, je plante. Si je libère le fournisseur ou l'imageRef après avoir créé l'UIImage, je ne vois pas de crash, mais ils passent apparemment les pixels complètement, donc je suis nerveux à propos de la libération de ces états intermédiaires.

(Je sais par CF docs que je devrais avoir besoin d'appeler release sur ces deux derniers parce qu'ils proviennent des fonctions Create, mais puis-je le faire avant l'utilisation de UIImage?) Je peux probablement utiliser le callloc callback du fournisseur nettoyer le tampon de pixels, mais quoi d'autre?

Merci!

Répondre

21

La règle du pouce ici est "-release * si vous n'en avez pas besoin".

Parce que vous ne avez plus besoin provider et imageRef après, vous devriez -release tous, à savoir

UIImage * image = [[UIImage alloc] initWithCGImage:imageRef]; 
CGDataProviderRelease(provider); 
CGImageRelease(imageRef); 
return [image autorelease]; 

pixel est pas géré par ref-comptage, de sorte que vous devez dire à l'API CG pour les libérer pour vous quand nécessaire. Pour ce faire:

void releasePixels(void *info, const void *data, size_t size) { 
    free((void*)data); 
} 
.... 

CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, pixels, pixelBufferSize, releasePixels); 

Par ailleurs, vous pouvez utiliser +imageWithCGImage: au lieu de [[[* alloc] initWithCGImage:] autorelease]. Encore mieux, il ya +imageWithData: donc vous n'avez pas besoin de jouer avec le CG et malloc choses.

(*. Sauf lorsque le retainCount est déjà soi-disant zéro depuis le début)

+0

Merci Kenny. C'est une description joliment succincte; Je pense que j'ai été un peu ébranlé par l'imprévisibilité du tampon de tas brut, mais comme toujours, faites confiance aux règles et vous serez récompensé. À votre santé. –

+1

Juste pour clarifier, les mots clés dans les fonctions de base sont "Créer" et "Nouveau". Si la fonction contient l'un de ces mots, vous devez libérer la mémoire renvoyée. La plupart des types de données Core sont compatibles avec CFType. Ce qui signifie que vous pouvez utiliser les appels Objective-C retain/release/autorelease sur eux si c'est plus facile. c'est-à-dire [(id) imageRef release]; ou CFRelease (imageRef); –

+0

N'oubliez pas de vérifier si 'imageRef' est' NULL' si vous utilisez 'CFRelease'. – kennytm

-2

Oui, ce code me rend mal à l'aise. En tant qu'ancien rule-to-live, j'essaie de ne pas mélanger et de faire correspondre C et C++, et C/Objective-C dans la même fonction/méthode/sélecteur.

Que diriez-vous de diviser cela en deux méthodes. Avoir changé cette makeAnImage en makeAnImageRef et remonter la création UIImage dans un autre sélecteur Obj-C.

+0

Bien que je comprends la réaction :) ne faisant toujours pas aider mon problème de gestion de la mémoire. J'ai toujours le problème que le fait de passer des octets bruts au début du pipeline me laisse avec un UIImage qui a toujours besoin de ces octets. Donc, ce code est plus lié qu'il n'y paraît. –

8
unsigned char * pixels = malloc(...); 

Vous possédez le tampon pixels parce que vous mallocked il.

CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, pixels, pixelBufferSize, NULL); 

Core Graphics suit les règles de base de la Fondation. Vous possédez le fournisseur de données parce que vous Created il.

Vous n'avez pas fourni de rappel de version, vous disposez donc toujours du tampon pixels. Si vous aviez fourni un rappel de version, l'objet CGDataProvider prendrait possession du tampon ici. (Généralement une bonne idée.)

CGImageRef imageRef = CGImageCreate(..., provider, ...); 

Vous possédez l'objet CGImage car vous l'avez créé.

UIImage * image = [[UIImage alloc] initWithCGImage:imageRef]; 

Vous possédez l'objet UIImage car vous l'avez alloué.

Vous possédez également l'objet CGImage. Si l'objet UIImage veut posséder l'objet CGImage, il le conservera ou fera sa propre copie.

return [image autorelease]; 

Vous abandonnez votre propriété de l'image. Donc, votre code fuit les pixels (vous n'avez pas transféré la propriété au fournisseur de données et vous ne les avez pas libérés vous-même), le fournisseur de données (vous ne l'avez pas publié) et le CGImage (vous n'avez pas t le relâcher). Une version fixe transfèrerait la propriété des pixels au fournisseur de données, et libérerait à la fois le fournisseur de données et la CGImage au moment où l'UIImage est prêt. Ou, utilisez simplement imageWithData:, comme le suggère KennyTM.

+0

Peter: Merci d'avoir pris le temps de passer à travers, surtout après la fin de la période de questions et réponses. –

1
unsigned char * pixels = malloc(...); 

J'ai aussi eu problème avec malloc/libre après avoir utilisé CGImageCreate j'ai finalement trouvé une solution simple et bonne. Je viens de remplacer la ligne:

CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, pixels, pixelBufferSize, NULL); 

avec:

NSData *data = [NSData dataWithBytes:pixels length:pixelBufferSize]; 
CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)data); 

Juste après que je pouvais libérer de la mémoire mallocked:

free (pixels); 
+1

Cela nécessite la copie de tous les octets, ce qui peut s'avérer inefficace dans certains cas d'utilisation. Au lieu de cela, utilisez [NSData dataWithBytesNoCopy: length] qui prendra en charge les octets, et les libérera pour vous quand ils ne sont plus nécessaires. –

Questions connexes