2009-05-29 10 views
116

Modifica febbraio 2014: Si noti che questa domanda risale a iOS 2.0! I requisiti e la gestione delle immagini sono passati molto da allora. Retina rende le immagini più grandi e caricandole leggermente più complesse. Con il supporto integrato per immagini iPad e retina, , dovresti sicuramente utilizzare ImageNamed nel tuo codice.Dispellendo l'immagine UIIName: FUD

Vedo un sacco di persone che dicono che imageNamed è un numero errato, ma un numero uguale di persone che dicono che le prestazioni sono buone, specialmente quando si esegue il rendering di UITableView s. Vedere this SO question per esempio o this article su iPhoneDeveloperTips.com

UIImage s' imageNamed metodo utilizzato a perdere così è stato meglio evitare, ma è stato risolto nella release più recenti. Mi piacerebbe capire meglio l'algoritmo di caching per prendere una decisione ragionata su dove posso fidarmi del sistema per mettere in cache le mie immagini e dove devo fare il miglio supplementare e farlo da solo. La mia attuale comprensione di base è che si tratta di un semplice NSMutableDictionary di UIImages a cui si fa riferimento per nome file. Diventa più grande e quando la memoria si esaurisce diventa molto più piccolo.

Ad esempio, qualcuno sa per certo che la cache di immagini dietro imageNamed non risponde a didReceiveMemoryWarning? Sembra improbabile che Apple non lo faccia.

Se si dispone di informazioni sull'algoritmo di memorizzazione nella cache, si prega di postarlo qui.

+1

Sono pieno di suspense. :) – Kriem

+2

anch'io. Sembra che SO non sia così pieno di geni come pensavo. –

+0

Sembra che tu abbia passato un po 'di tempo a indagare su questo. Hai fatto qualche esperimento che mostrasse qualsiasi impatto negativo di UIImage imageNamed con l'ultima versione di cacao-touch? Creare un UITableView e aggiungere un sacco (molte migliaia) di righe e vedere se il rendimento va giù quando si mostra un'immagine diversa in ogni riga. Forse allora le persone possono commentare le tue scoperte. – stefanB

risposta

86

tldr: ImagedNamed va bene. Gestisce bene la memoria. Usalo e smettila di preoccuparti.

Modifica Nov 2012: nota che questa domanda risale a iOS 2.0! I requisiti e la gestione delle immagini sono passati molto da allora. Retina rende le immagini più grandi e caricandole leggermente più complesse. Con il supporto integrato per le immagini iPad e retina, dovresti sicuramente usare ImageNamed nel tuo codice. Ora, per i posteri:

Lo sister thread sui forum Apple Dev ha ricevuto un traffico migliore. Nello specifico, Rincewind ha aggiunto un po 'di autorità.

Ci sono problemi in iPhone OS 2.x in cui imageNamed: cache non viene cancellato, anche dopo un avviso di memoria. Allo stesso tempo + imageNamed: ha ottenuto un sacco di utilizzo non per la cache, ma per la comodità, che ha probabilmente ingrandito il problema più di quanto avrebbe dovuto essere.

pur avvertendo che

Sul fronte velocità, c'è un malinteso generale di ciò che sta accadendo. La cosa più grande che + imageNamed: fa è decodificare i dati dell'immagine dal file sorgente, che gonfia quasi sempre in modo significativo la dimensione dei dati (ad esempio, un file PNG di dimensioni dello schermo potrebbe consumare poche decine di KB quando compresso, ma consuma più di mezzo MB decompresso - larghezza * altezza * 4). Al contrario + imageWithContentsOfFile: decomprimerà quell'immagine ogni volta che saranno necessari i dati dell'immagine. Come puoi immaginare, se hai bisogno dei dati dell'immagine solo una volta, non hai vinto nulla qui, tranne che per avere una versione cache dell'immagine che gira intorno, e probabilmente più a lungo del necessario. Tuttavia, se si dispone di un'immagine grande che è necessario ridisegnare spesso, allora ci sono alternative, anche se quella che raccomanderei principalmente è di evitare di ridisegnare quella grande immagine :).

Rispetto al comportamento generale della cache, fa cache in base al nome file (quindi due istanze di + imageNamed: con lo stesso nome dovrebbe comportare riferimenti agli stessi dati memorizzati nella cache) e la cache crescerà dinamicamente come richiedere più immagini tramite + imageNamed :. Su iPhone OS 2.x un bug impedisce che la cache venga ridotta quando viene ricevuto un avviso di memoria.

e

mia comprensione è che il + imageNamed: cache deve rispettare avvisi di memoria su iPhone OS 3.0. Provalo quando ne hai la possibilità e segnala bug se trovi che questo non è il caso.

Quindi, ce l'hai. imageNamed: non distruggerà le finestre o ucciderà i tuoi figli. È piuttosto semplice ma è uno strumento di ottimizzazione. Purtroppo è mal chiamato e non c'è equivaluent che è facile da usare - da qui la gente uso eccessivo e si arrabbiano quando lo fa semplicemente il suo lavoro

ho aggiunto una categoria per UIImage per risolvere questo:

// 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]]; 
} 

Scuotewind ha anche incluso alcuni esempi di codice per creare la tua versione ottimizzata. Non riesco a vederlo vale la maintentace, ma qui è per completezza.

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); 

Il compromesso con questo codice è che l'immagine decodificata utilizza più memoria ma il rendering è più veloce.

+0

Sembra che su iOS11 [UIImage imageNamed:] non stia rubando immagini dalla cache se le immagini provengono dal catalogo xcasset. Questo porta a crash a causa di memoria insufficiente. –

5

Nella mia esperienza, la cache di immagini creata da imageNamed non risponde agli avvisi di memoria. Ho avuto due applicazioni che erano così magre come potrei averle per quanto riguarda la gestione della mem, ma erano ancora inspiegabilmente in crash a causa della mancanza di mem. Quando ho smesso di usare imageNamed per caricare le immagini, entrambe le applicazioni sono diventate drammaticamente più stabili.

Ammetto che entrambe le applicazioni hanno caricato immagini piuttosto grandi, ma nulla che sarebbe totalmente fuori dall'ordinario. Nella prima applicazione, ho saltato del tutto il caching perché era improbabile che un utente tornasse alla stessa immagine due volte. Nel secondo, ho creato una classe di caching molto semplice facendo proprio quello che hai menzionato, mantenendo UIImages in un NSMutableDictionary e poi scaricando i suoi contenuti se ricevevo un avviso di memoria. Se imageNamed: dovessi memorizzare nella cache in quel modo, allora non avrei dovuto vedere alcun aggiornamento delle prestazioni. Tutto questo era in esecuzione su 2.2 - Non so se ci sono implicazioni 3.0 su questo.

Potete trovare la mia altra domanda a questo problema dalla mia prima applicazione qui: StackOverflow question about UIImage cacheing

Un'altra nota - InterfaceBuilder utilizza imageNamed sotto le coperte. Qualcosa da tenere a mente se si incontra questo problema.

+0

Ho visto esattamente lo stesso comportamento e la lettura dal file lo ha risolto direttamente. Inoltre, succede quando tento di caricare un'immagine molto grande - 11.456 x 3.226 px, 15 MB. La mia teoria è che il sistema esaurisce la memoria in parte attraverso l'operazione cache ImageNamed. E non c'è gestione integrata per questo caso. – Dogweather