Dissipazione dell'immagine UIImage Nome: FUD


117

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

Vedo molte persone che dicono che imageNamedè male, ma un numero uguale di persone dice che le prestazioni sono buone, specialmente durante il rendering UITableView. Vedi questa domanda SO per esempio o questo articolo su iPhoneDeveloperTips.com

UIImageIl imageNamedmetodo utilizzato per la perdita è stato quindi evitato, ma è stato corretto nelle versioni recenti. Mi piacerebbe capire meglio l'algoritmo di caching per prendere una decisione ragionata su dove posso fidarmi del sistema per memorizzare nella cache le mie immagini e dove devo fare il possibile e farlo da solo. Il mio attuale comprensione di base è che si tratta di un semplice NSMutableDictionarydi UIImagesriferimento per nome di file. Diventa più grande e quando la memoria si esaurisce diventa molto più piccola.

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

Se hai informazioni sull'algoritmo di memorizzazione nella cache, pubblicalo qui.


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

Sembra che tu abbia passato un po 'di tempo a indagare su questo. Hai fatto qualche sperimentazione che mostrerebbe un impatto negativo dell'immagine UIImage Nominata con l'ultima versione cocoa-touch? Crea un UITableView e aggiungi molte (molte migliaia) righe e verifica se le prestazioni diminuiscono quando mostri un'immagine diversa in ogni riga. Forse allora le persone possono commentare le tue scoperte.
stefanB

In realtà, non ho fatto molti esperimenti. Uso imageNamed e anche se devo ancora ottimizzare la memoria di questa app, non ho nulla che possa puntare direttamente a imageNamed come un maiale di memoria. Ho indicato alcuni post di blog in cui si lamentava imagedNamed qui e ho posto una domanda simile sulle bacheche Apple che hanno ottenuto più azione. Ripubblicherò un riepilogo e collegherò qui quando avrò il tempo e sentirò che ha ricevuto le risposte che riceverà.
Rog,

Risposte:


85

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

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

Il thread gemello sui forum di sviluppo di Apple ha ricevuto un traffico migliore. Nello specifico Scuotivento ha aggiunto una certa autorità.

Ci sono problemi in iPhone OS 2.x in cui la cache imageNamed: non verrebbe cancellata, anche dopo un avviso di memoria. Allo stesso tempo + imageNamed: è stato utilizzato molto non per la cache, ma per comodità, che probabilmente ha ingrandito il problema più di quanto avrebbe dovuto essere.

pur avvertendolo

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

Per quanto riguarda il comportamento generale della cache, esegue la cache in base al nome del file (quindi due istanze di + imageNamed: con lo stesso nome dovrebbero risultare riferimenti agli stessi dati memorizzati nella cache) e la cache crescerà dinamicamente man mano che si richiedono più immagini tramite + imageNamed :. Su iPhone OS 2.xa un bug impedisce che la cache venga ridotta quando viene ricevuto un avviso di memoria.

e

La mia comprensione è che + imageNamed: cache dovrebbe rispettare gli avvisi di memoria su iPhone OS 3.0. Provalo quando ne hai la possibilità e segnala i bug se trovi che non è così.

Così il gioco è fatto. imageNamed: non romperà le finestre né ucciderà i tuoi figli. È piuttosto semplice ma è uno strumento di ottimizzazione. Purtroppo ha un nome sbagliato e non esiste un equivalente che sia così facile da usare, quindi le persone lo abusano e si arrabbiano quando semplicemente fa il suo lavoro

Ho aggiunto una categoria a UIImage per risolvere il problema:

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

Scuotivento ha incluso anche un codice di esempio per creare la tua versione ottimizzata. Non vedo che valga la pena, 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.


Sembra che su iOS11, [UIImage imageNamed:] non rimuova le immagini dalla cache se le immagini provengono dal catalogo xcasset. Ciò porta a arresti anomali dovuti a memoria insufficiente.
Juraj Antas

5

Nella mia esperienza, la cache delle immagini creata da imageNamed non risponde agli avvisi di memoria. Ho avuto due applicazioni che erano il più snelle possibile per quanto riguarda la gestione dei mem, ma erano ancora inspiegabilmente bloccati a causa della mancanza di mem. Quando ho smesso di usare imageNamed per caricare le immagini, entrambe le applicazioni sono diventate notevolmente più stabili.

Devo ammettere che entrambe le applicazioni hanno caricato immagini piuttosto grandi, ma niente che sarebbe totalmente fuori dall'ordinario. Nella prima applicazione, ho saltato del tutto la memorizzazione nella cache perché era improbabile che un utente tornasse due volte alla stessa immagine. Nel secondo, ho creato una classe di memorizzazione nella cache davvero semplice facendo proprio quello che hai menzionato: mantenere UIImages in un NSMutableDictionary e quindi svuotare il suo contenuto se ho ricevuto un avviso di memoria. Se imageNamed: fosse stato memorizzato nella cache in questo modo, 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.

Puoi trovare la mia altra domanda su questo problema dalla mia prima app qui: Domanda StackOverflow sulla cache di UIImage

Un'altra nota: InterfaceBuilder usa imageNamed sotto le coperte. Qualcosa da tenere a mente se incontri questo problema.


Ho visto lo stesso identico 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 esaurisca la memoria in parte durante l'operazione di cache ImageNamed. E non c'è nessuna gestione integrata per questo caso.
Dogweather
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.