ABONAMENTE VIDEO REDACȚIA
RO
EN
NOU
Numărul 148
Numărul 147 Numărul 146 Numărul 145 Numărul 144 Numărul 143 Numărul 142 Numărul 141 Numărul 140 Numărul 139 Numărul 138 Numărul 137 Numărul 136 Numărul 135 Numărul 134 Numărul 133 Numărul 132 Numărul 131 Numărul 130 Numărul 129 Numărul 128 Numărul 127 Numărul 126 Numărul 125 Numărul 124 Numărul 123 Numărul 122 Numărul 121 Numărul 120 Numărul 119 Numărul 118 Numărul 117 Numărul 116 Numărul 115 Numărul 114 Numărul 113 Numărul 112 Numărul 111 Numărul 110 Numărul 109 Numărul 108 Numărul 107 Numărul 106 Numărul 105 Numărul 104 Numărul 103 Numărul 102 Numărul 101 Numărul 100 Numărul 99 Numărul 98 Numărul 97 Numărul 96 Numărul 95 Numărul 94 Numărul 93 Numărul 92 Numărul 91 Numărul 90 Numărul 89 Numărul 88 Numărul 87 Numărul 86 Numărul 85 Numărul 84 Numărul 83 Numărul 82 Numărul 81 Numărul 80 Numărul 79 Numărul 78 Numărul 77 Numărul 76 Numărul 75 Numărul 74 Numărul 73 Numărul 72 Numărul 71 Numărul 70 Numărul 69 Numărul 68 Numărul 67 Numărul 66 Numărul 65 Numărul 64 Numărul 63 Numărul 62 Numărul 61 Numărul 60 Numărul 59 Numărul 58 Numărul 57 Numărul 56 Numărul 55 Numărul 54 Numărul 53 Numărul 52 Numărul 51 Numărul 50 Numărul 49 Numărul 48 Numărul 47 Numărul 46 Numărul 45 Numărul 44 Numărul 43 Numărul 42 Numărul 41 Numărul 40 Numărul 39 Numărul 38 Numărul 37 Numărul 36 Numărul 35 Numărul 34 Numărul 33 Numărul 32 Numărul 31 Numărul 30 Numărul 29 Numărul 28 Numărul 27 Numărul 26 Numărul 25 Numărul 24 Numărul 23 Numărul 22 Numărul 21 Numărul 20 Numărul 19 Numărul 18 Numărul 17 Numărul 16 Numărul 15 Numărul 14 Numărul 13 Numărul 12 Numărul 11 Numărul 10 Numărul 9 Numărul 8 Numărul 7 Numărul 6 Numărul 5 Numărul 4 Numărul 3 Numărul 2 Numărul 1
×
▼ LISTĂ EDIȚII ▼
Numărul 22
Abonament PDF

iOS image caching. Libraries benchmark

Bogdan Poplauschi
Senior iOS Developer
@Yardi România



PROGRAMARE


În ultimii ani, tendința aplicațiilor iOS se îndreaptă spre un design cât mai interactiv și plăcut ochiului. Deoarece prezentarea imaginilor este un element cheie în tot acest proces, majoritatea aplicațiilor folosesc imagini care trebuie downloadate și afișate. Foarte mulți developeri au fost puși la un moment dat în situația de a-și popula controalele UI cu diferite imagini. Descărcarea de astfel de imagini consumă destul de multe resurse, cum ar fi date din serviciul de internet mobil, baterie, CPU. Prin urmare, din nevoia de a minimiza consumul acestor resurse, s-a dezvoltat așa numitul pattern cache.

Pentru a dobândi un user experience cât mai bun, este foarte important să înțelegem ce se întâmplă în interiorul sistemului iOS, atunci când stocăm imagini în cache sau le încărcăm.

În plus, consider că un set de benchmarks pentru bibliotecile open-source de image caching poate fi foarte util în alegerea soluției potrivite.

2. Abordarea Clasică

  • se downloadează imaginile în mod asincron.
  • se procesează imaginile pentru a fi afișate (modificarea dimensiunii, îndepărtarea efectului de ochi roșii, îndepărtarea marginilor etc.).
  • se stochează imaginile pe flash drive (unitatea internă de stocare).
  • se citesc de pe flash drive și se afișează la cerere.
// assuming we have an NSURL *imageUrl and UIImageView *imageView, we need to load the image from the URL and display it in the imageView 
if ([self hasImageDataForURL:imageUrl] {
   
	NSData *data = [self imageDataForUrl:imageUrl];
  
	UIImage *image = [UIImage imageWithData:imageData];
   
	dispatch_async(dispatch_get_main_queue(), ^{
     imageView.image = image;
   });
	
} else {
	
   [self downloadImageFromURL:imageUrl withCompletion:^(NSData *imageData, …) 
	{
     [self storeImageData:imageData …];
     
	UIImage *image = [UIImage imageWithData:imageData];
     
	dispatch_async(dispatch_get_main_queue(), ^{
       
	imageView.image = image;
     
	});
   
	}];
}

Matematica FPS

- 60 FPS este idealul pentru orice actualizare de UI, pentru a asigura o experiență fără cusur.

- 60 FPS => 16.7 ms/cadru. Acest lucru înseamnă că, dacă oricare dintre operațiunile de pe main queue durează mai mult de 16.7 ms, FPS-ul la scroll scade vizibil (efect de sacadare) deoarece CPU se va ocupa de alte operații, și nu de actualizarea UI.

3. Dezavantajele abordării clasice

Încărcarea imaginilor sau a fișierelor în general, de pe flash drive, e o operație costisitoare (accesarea flash drive-urilor este mult mai lentă decât cea a memoriei RAM).

Crearea unei instanțe Ullmage va avea ca rezultat o versiune comprimată a imaginii, mapată pe o secțiune de memorie (mapped memory). Imaginea comprimată este mică și nu poate fi afișată direct. În cazul încărcării de pe flash drive, imaginea este doar mapată, urmând ca încărcarea în memorie să se facă la cerere. Decomprimarea unei imagini este de asemenea costisitoare.

Atribuirea proprietății "image" din imageView în acest caz va crea un CATransaction care va fi înregistrat în run loop. La următoarea iterație, executarea CATransaction-ului implică (în funcție de imagine), crearea unor copii ale tuturor imaginilor care au fost setate ca layer contents. Copierea de imagini include:

  • alocarea de buffere pentru scriere/citire de fișiere și decomprimare,
  • citirea datelor de pe flash drive în memorie,
  • decomprimarea de imagini (rezultă un raw bitmap) - implică un consum ridicat de CPU,
  • imaginile nealiniate corect pe biți sunt copiate de către CoreAnimation pentru a fi corectate și redate corespunzător. Acest lucru nu este menționat în Apple docs, dar folosirea Instruments arată apeluri CA::Render::copy_image chiar și atunci când instrumentul CoreAnimation nu indică nicio imagine copiată.
  • începând cu iOS 7, decodorul hardware JPEG mai este accesibil doar aplicațiilor sistem. Aceasta înseamnă că aplicațiile noastre se bazează pe un decodor software care este mult mai încet. Acest aspect a fost documentat de către echipa FastImageCache pe pagina lor Github, dar și de Nick Lockwood într-un post pe Twitter.

4. O soluție robustă de caching a imaginilor în iOS ar trebui să:

  • descarce imagini în mod asincron, astfel încât main queue să fie folosit cât mai puțin posibil,
  • decomprime imaginile pe un background queue. Acest lucru nu e deloc simplu. Pentru mai multe detalii, accesați http://www.cocoanetics.com/2011/10/avoiding-image-decompression-sickness/ .
  • stocheze imaginea atât în cache-ul din memorie, cât și în cel de pe flash drive. Procesul de caching pe flash drive este important deoarece aplicația ar putea fi închisă sau ar putea fi nevoită să elibereze memorie din cauza resurselor limitate. În acest caz, re-încărcarea imaginilor de pe flash drive este mult mai rapidă decât descărcarea lor.

Notă: dacă folosiți NSCache pentru memoria cache, această clasă va elibera toate resursele referențiate, în caz de memory warning. Pentru mai multe detalii despre NSCache, accesați http://nshipster.com/nscache/ .

  • stocheze imaginea decomprimată pe flash drive și în memorie pentru a evita repetarea procesului de decomprimare.
  • folosească GCD și block-uri. Acestea fac codul mai performant, mai ușor de citit și de scris. În prezent, GCD și block-urile sunt indispensabile pentru operațiile asincrone.
  • bonus 1: categoria peste UllmageView pentru a face integrarea ușoară.
  • bonus 2: capacitatea de a procesa imaginile după descărcare și înainte de a le stoca în cache.

Folosirea avansată a imaginilor pe iOS

Pentru a afla mai multe despre folosirea de imagini pe iOS, despre cum funcționează framework-urile din cadrul iOS SDK (CoreGraphics, Image IO, CoreAnimation, CoreImage), CPU vs. GPU și altele, citiți acest articol bun, scris de @rsebbe.

Este Core Data un candidat valid?

Aici aveți un benchmark pentru caching de imagini folosind Core Data versus File System. Rezultatele recomandă File System (după cum ne așteptam).

5. Benchmarks

Doar uitându-ne la conceptele de mai sus înțelegem că e destul de greu să scrii o asemenea componentă de unul singur; mai mult, va lua mult timp și va fi extrem de dificil. Tocmai de aceea apelăm la soluții open source. Majoritatea dintre voi ați auzit de SDWebImage sau FastImageCache. Pentru a decide care vi se potrivește mai bine, le-am comparat și am analizat felul în care răspund cerințelor noastre.

Bibliotecile testate

Notă: AFNetworking a fost adăugat comparației pentru că, începând cu iOS7, oferă și un cache de tip flash drive (NSURLCache).

Scenariul

Pentru fiecare bibliotecă am realizat o instalare de la zero a aplicației de benchmark, apoi am pornit aplicația, scroll încet, până ce imaginile s-au încărcat, apoi scroll repede / încet alternativ. Am închis apoi aplicația pentru a forța încărcarea de pe flash drive (când aceasta era disponibilă), apoi am rulat din nou același scenariu de scroll.

Aplicația benchmark - proiect

Sursa proiectului demo poate fi găsită pe Github sub denumirea de ImageCachingBenchmark, împreună cu diagramele, tabelele cu date și altele.

Sursele proiectului de pe Github au trebuit modificate, la fel ca cele ale bibliotecilor, adăugându-se tipul cache-ului din care s-a făcut încărcarea. Nedorind să includ în repository și sursele Cocoapods (acest lucru nu este recomandat) și pentru că proiectul trebuia să fie compilabil după instalarea clean a Cocoapods, varianta de proiect din Github este ușor diferită de cea cu care s-au efectuat măsurătorile.

Dacă unii dintre voi doresc să ruleze din nou benchmark-urile, va trebui să creați un completionBlock pentru încărcarea imaginilor pentru toate bibliotecile, similar celui default din SDWebImage, care returnează tipul SDImage CacheType.

Rezultatele

Rezultatele complete pot fi găsite pe pagina proiectului Github. Deoarece acele tabele sunt foarte mari, am decis să creez diagrame folosind date de la cel mai rapid (iPhone 5s) și cel mai lent device (iPhone 4).

Rezultate pentru iPhone 5s

Notă: Prin disk înțelegem flash drive (dispozitivul de stocare internă).

Rezultate pentru iPhone 4

Legendă

  • async download = biblioteca oferă suport pentru descărcare asincronă.
  • backgr decompr = decomprimarea imaginilor se face pe un background queue/thread.
  • store decompr = imaginile sunt stocate in varianta decomprimată.
  • memory/flash drive cache = suport pentru cache pe flash drive sau în memorie.
  • UIImageView categ = biblioteca include o categorie peste UIImageView.
  • from memory/flash drive = au obținut cele mai bune rezultate la timpii de încărcare din cache (memorie sau flash drive).

6. Concluzii

Elaborarea unei componente de caching de imagini pentru iOS este dificilă.

SDWebImage și AFNetworking sunt proiecte serioase, cu mulți contribuitori și care sunt întreținute în mod corect. FastImageCache se îndreaptă spre același statut destul de repede. Dacă analizăm informațiile oferite mai sus, cred că putem fi de accord că SDWebImage este cea mai bună soluție la momentul actual, chiar dacă pentru unele proiecte AFNetworking sau FastImageCache s-ar potrivi mai bine. Totul depinde de cerințele proiectului în cauză.

Link-uri utile

https://github.com/rs/SDWebImage

https://github.com/path/FastImageCache

https://github.com/AFNetworking/AFNetworking

https://github.com/tumblr/TMCache

https://github.com/hpique/Haneke

http://bpoplauschi.wordpress.com/2014/03/21/ios-image-caching-sdwebimage-vs-fastimage/

https://github.com/bpoplauschi/ImageCachingBenchmark

Conferință TSM

NUMĂRUL 147 - Automotive

Sponsori

  • Accenture
  • BT Code Crafters
  • Accesa
  • Bosch
  • Betfair
  • MHP
  • BoatyardX
  • .msg systems
  • P3 group
  • Ing Hubs
  • Cognizant Softvision
  • Colors in projects