117 votes

Dissiper le nom de l'image UIImage : FUD

Edit Feb 2014 : Notez que cette question date d'iOS 2.0 ! Les exigences en matière d'image et de traitement ont beaucoup évolué depuis lors. La technologie Retina rend les images plus grandes et leur chargement légèrement plus complexe. Avec la prise en charge intégrée de l'iPad et des images rétina, vous devriez certainement utiliser ImageNamed dans votre code .

Je vois beaucoup de gens qui disent imageNamed est mauvais, mais un nombre égal de personnes disent que les performances sont bonnes, surtout lors du rendu. UITableView s. Voir cette question SO par exemple ou cet article sur iPhoneDeveloperTips.com

UIImage 's imageNamed avait l'habitude de fuir, il était donc préférable de l'éviter, mais cela a été corrigé dans les versions récentes. J'aimerais mieux comprendre l'algorithme de mise en cache afin de prendre une décision raisonnée sur les cas où je peux faire confiance au système pour mettre mes images en cache et ceux où je dois aller plus loin et le faire moi-même. D'après ce que je comprends actuellement, il s'agit d'un simple NSMutableDictionary de UIImages référencé par le nom du fichier. Il devient plus grand et quand la mémoire s'épuise, il devient beaucoup plus petit.

Par exemple, est-ce que quelqu'un sait avec certitude que le cache d'image derrière imageNamed ne répond pas à didReceiveMemoryWarning ? Il semble peu probable qu'Apple ne le fasse pas.

Si vous avez des informations sur l'algorithme de mise en cache, veuillez les publier ici.

86voto

Roger Nolan Points 10248

tldr : ImagedNamed est bien. Il gère bien la mémoire. Utilisez-le et arrêtez de vous inquiéter.

Edition Nov 2012 : Notez que cette question date d'iOS 2.0 ! Les exigences et le traitement des images ont beaucoup évolué depuis. La technologie Retina rend les images plus grandes et leur chargement légèrement plus complexe. Avec la prise en charge intégrée de l'iPad et des images rétina, vous devriez certainement utiliser ImageNamed dans votre code. Maintenant, pour la postérité :

El fil conducteur sur les forums de développement d'Apple a reçu un meilleur trafic. Plus précisément Rincewind a ajouté une certaine autorité.

Il y a des problèmes dans iPhone OS 2.x où le cache imageNamed : ne serait pas effacé, même après un avertissement de mémoire. En même temps, +imageNamed : a été beaucoup utilisé, non pas pour le cache, mais pour sa commodité, ce qui a probablement amplifié le problème plus qu'il n'aurait dû l'être.

tout en avertissant que

Sur le front de la vitesse, il y a une incompréhension générale de ce qui se passe. La plus grande chose que +imageNamed : fait est de décoder les données de l'image à partir du fichier source, ce qui gonfle presque toujours de manière significative la taille des données (par exemple, un fichier PNG de taille écran peut consommer quelques dizaines de Ko lorsqu'il est compressé, mais consomme plus d'un demi-moteur décompressé - largeur * hauteur * 4). En revanche, +imageWithContentsOfFile : décompressera cette image chaque fois que les données de l'image seront nécessaires. Comme vous pouvez l'imaginer, si vous n'avez besoin des données de l'image qu'une seule fois, vous n'avez rien gagné ici, si ce n'est d'avoir une version en cache de l'image qui traîne, et probablement plus longtemps que ce dont vous avez besoin. Cependant, si vous avez une grande image que vous devez redessiner souvent, alors il y a des alternatives, bien que celle que je recommanderais principalement soit d'éviter de redessiner cette grande image :).

En ce qui concerne le comportement général du cache, il est basé sur le nom de fichier (ainsi, deux instances de +imageNamed : avec le même nom devraient donner lieu à des références aux mêmes données mises en cache) et le cache s'agrandit dynamiquement au fur et à mesure que vous demandez d'autres images via +imageNamed :. Sur l'iPhone OS 2.x, un bogue empêche le cache d'être réduit lorsqu'un avertissement de mémoire est reçu.

et

D'après ce que j'ai compris, le +imageNamed : cache devrait respecter les avertissements de mémoire sur iPhone OS 3.0. Testez-le quand vous en aurez l'occasion et signalez les bogues si vous constatez que ce n'est pas le cas.

Donc, vous l'avez. imageNamed : ne va pas briser votre Windows ou assassiner vos enfants. C'est assez simple mais c'est un outil d'optimisation. Malheureusement, son nom est mal choisi et il n'existe pas d'équivalent aussi facile à utiliser - c'est pourquoi les gens en abusent et s'énervent lorsqu'il fait simplement son travail.

J'ai ajouté une catégorie à UIImage pour corriger cela :

// header omitted
// Before you waste time editing this, please remember that a semi colon at the end of a method definition is valid and a matter of style.
+ (UIImage*)imageFromMainBundleFile:(NSString*)aFileName; {
    NSString* bundlePath = [[NSBundle mainBundle] bundlePath];
    return [UIImage imageWithContentsOfFile:[NSString stringWithFormat:@"%@/%@", bundlePath,aFileName]];
}

Rincewind a également inclus quelques exemples de code pour construire votre propre version optimisée. Je ne pense pas que cela vaille la peine de le maintenir, mais le voici pour être complet.

CGImageRef originalImage = uiImage.CGImage;
CFDataRef imageData = CGDataProviderCopyData(
     CGImageGetDataProvider(originalImage));
CGDataProviderRef imageDataProvider = CGDataProviderCreateWithCFData(imageData);
CFRelease(imageData);
CGImageRef image = CGImageCreate(
     CGImageGetWidth(originalImage),
     CGImageGetHeight(originalImage),
     CGImageGetBitsPerComponent(originalImage),
     CGImageGetBitsPerPixel(originalImage),
     CGImageGetBytesPerRow(originalImage),
     CGImageGetColorSpace(originalImage),
     CGImageGetBitmapInfo(originalImage),
     imageDataProvider,
     CGImageGetDecode(originalImage),
     CGImageGetShouldInterpolate(originalImage),
     CGImageGetRenderingIntent(originalImage));
CGDataProviderRelease(imageDataProvider);
UIImage *decompressedImage = [UIImage imageWithCGImage:image];
CGImageRelease(image);

Le compromis avec ce code est que l'image décodée utilise plus de mémoire mais que le rendu est plus rapide.

5voto

Bdebeez Points 2066

D'après mon expérience, le cache d'image créé par imageNamed ne répond pas aux avertissements de mémoire. J'ai eu deux applications qui étaient aussi légères que possible en ce qui concerne la gestion de la mémoire, mais qui plantaient toujours inexplicablement à cause du manque de mémoire. Lorsque j'ai arrêté d'utiliser imageNamed pour charger les images, les deux applications sont devenues beaucoup plus stables.

J'admets que les deux applications ont chargé des images un peu grandes, mais rien qui ne sorte de l'ordinaire. Dans la première application, j'ai simplement ignoré la mise en cache car il était peu probable qu'un utilisateur revienne deux fois sur la même image. Dans la seconde, j'ai construit une classe de mise en cache très simple qui fait exactement ce que vous avez mentionné - en conservant les UIImages dans un NSMutableDictionary et en vidant son contenu si je reçois une alerte mémoire. Si imageNamed : devait mettre en cache de cette manière, je n'aurais pas dû voir d'amélioration des performances. Tout cela s'est déroulé sous la version 2.2 - je ne sais pas s'il y a des implications de la version 3.0 à ce sujet.

Vous pouvez trouver mon autre question autour de ce problème de ma première application ici : Question StackOverflow sur la mise en cache de UIImage

Une autre remarque - InterfaceBuilder utilise imageNamed sous le manteau. C'est une chose à garder à l'esprit si vous rencontrez ce problème.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X