Próbuję poprawić wydajność mojej intensywnej dla obrazu aplikacji na telefon iPhone, używając pamięci podręcznej obrazów na dysku zamiast przechodzenia przez sieć. Modelowałem pamięć podręczną obrazów po SDImageCache (http://github.com/rs/SDWebImage/blob/master/SDImageCache.m) i jest prawie taki sam, ale bez asynchronicznych operacji wejścia/wyjścia pamięci podręcznej.Przewiń widok i wydajność widoku tabeli podczas ładowania obrazów z dysku
Mam niektóre widoki przewijania i widoki tabel, które ładują te obrazy asynchronicznie. Jeśli obraz znajduje się na dysku, zostanie załadowany z pamięci podręcznej obrazów, w przeciwnym razie zostanie wysłane żądanie sieciowe, a wynik zostanie zapisany w pamięci podręcznej.
Problem, który napotykam, polega na tym, że podczas przewijania widoków przewijania lub widoków tabel zauważalne jest opóźnienie, gdy obraz jest ładowany z dysku. W szczególności animacja przechodzenia z jednej strony na drugą w widoku przewijania ma małe zamrożenie w połowie przejścia.
Próbowałem to naprawić przez:
- Używanie NSOperationQueue i NSInvocationOperation obiektów, aby wnioski o dostęp do dysku (w taki sam sposób jak SDImageCache), ale to nie pomaga z opóźnieniem w wszystko.
- Poprawianie kodu kontrolera widoku przewijania tak, aby ładował on tylko obrazy, gdy widok przewijania nie jest już przewijany. Oznacza to, że dostęp do dysku jest uruchamiany tylko wtedy, gdy przewijany widok przestaje się przewijać, ale jeśli natychmiast spróbuję przewinąć do następnej strony, mogę zauważyć opóźnienie wczytywania obrazu z dysku.
Czy istnieje sposób, aby mój dostęp do dysku był lepszy lub miał mniejszy wpływ na interfejs użytkownika?
Zauważ, że już buforuję obrazy również w pamięci. Więc gdy wszystko zostanie załadowane do pamięci, interfejs użytkownika jest miły i elastyczny. Ale gdy uruchamiana jest aplikacja lub są wysyłane ostrzeżenia o małej ilości pamięci, napotkam wiele opóźnień interfejsu, ponieważ obrazy są ładowane z dysku.
Odpowiednie fragmenty kodu znajdują się poniżej. Nie sądzę, że robię coś wyszukanego lub zwariowanego. Opóźnienie nie wydaje się być zauważalne na telefonie iPhone 3G, ale jest wyraźnie widoczne na iPodzie Touch drugiej generacji.
Zdjęcie Kod buforowanie:
Oto odnośny fragment mojego kodu buforowania obrazu. Całkiem proste.
- (BOOL)hasImageDataForURL:(NSString *)url {
return [[NSFileManager defaultManager] fileExistsAtPath:[self cacheFilePathForURL:url]];
}
- (NSData *)imageDataForURL:(NSString *)url {
NSString *filePath = [self cacheFilePathForURL:url];
// Set file last modification date to help enforce LRU caching policy
NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
[attributes setObject:[NSDate date] forKey:NSFileModificationDate];
[[NSFileManager defaultManager] setAttributes:attributes ofItemAtPath:filePath error:NULL];
return [NSData dataWithContentsOfFile:filePath];
}
- (void)storeImageData:(NSData *)data forURL:(NSString *)url {
[[NSFileManager defaultManager] createFileAtPath:[self cacheFilePathForURL:url] contents:data attributes:nil];
}
Przewiń kod widok kontroler
Oto odnośny fragment kodu, który używam do wyświetlania obrazów w moim przekonaniu przewijania kontrolerów.
- (void)scrollViewDidScroll:(UIScrollView *)theScrollView {
CGFloat pageWidth = theScrollView.frame.size.width;
NSUInteger index = floor((theScrollView.contentOffset.x - pageWidth/2)/pageWidth) + 1;
[self loadImageFor:[NSNumber numberWithInt:index]];
[self loadImageFor:[NSNumber numberWithInt:index + 1]];
[self loadImageFor:[NSNumber numberWithInt:index - 1]];
}
- (void)loadImageFor:(NSNumber *)index {
if ([index intValue] < 0 || [index intValue] >= [self.photoData count]) {
return;
}
// ... initialize an image loader object that accesses the disk image cache or makes a network request
UIView *iew = [self.views objectForKey:index];
UIImageView *imageView = (UIImageView *) [view viewWithTag:kImageViewTag];
if (imageView.image == nil) {
NSDictionary *photo = [self.photoData objectAtIndex:[index intValue]];
[loader loadImage:[photo objectForKey:@"url"]];
}
}
Ładowarka obiekt obraz jest po prostu lekki przedmiot, który sprawdza pamięć podręczną dysku i decyduje, czy należy pobrać obraz z dysku lub sieci.Raz to zrobić, to wywołuje metodę na kontrolerze widoku przewijania do wyświetlania obrazu:
- (void)imageLoadedFor:(NSNumber *)index image:(UIImage *)image {
// Cache image in memory
// ...
UIView *view = [self.views objectForKey:index];
UIImageView *imageView = (UIImageView *) [view viewWithTag:kImageViewTag];
imageView.contentMode = UIViewContentModeScaleAspectFill;
imageView.image = image;
}
UPDATE
byłem eksperymentuje z aplikacją, a ja wyłączyć pamięć podręczną obrazu i powrócił do zawsze podejmowania żądania sieciowe. Wygląda na to, że zwykłe użycie żądań sieciowych do pobierania obrazów to , powodując również opóźnienie podczas przewijania widoków przewijania i widoków tabel! Oznacza to, że po zakończeniu żądania sieciowego i wyświetleniu obrazu na stronie przewijania lub komórce tabeli interfejs jest nieco opóźniony i ma kilka sekund opóźnienia, gdy próbuję go przeciągnąć.
Opóźnienie wydaje się być bardziej zauważalne podczas korzystania z pamięci podręcznej dysku, ponieważ opóźnienie występuje zawsze przy przejściu strony. Być może robię coś nie tak podczas przypisywania załadowanego obrazu do odpowiedniego UIImageView?
Również - Próbowałem używać małych obrazów (miniatury 50x50), a opóźnienie wydaje się poprawiać. Wygląda więc na to, że wydajność osiąga się przez załadowanie dużego obrazu z dysku lub załadowanie dużego obrazu do obiektu UIImage. Sądzę, że jednym z ulepszeń byłoby zmniejszenie rozmiaru obrazów ładowanych do widoku przewijania i widoków tabel, co jednak planowałem zrobić. Jednak nie rozumiem, w jaki sposób inne aplikacje intensywnie korzystające z fotografii mogą wyświetlać zdjęcia wyglądające na dość wysokiej rozdzielczości w przewijanych widokach bez problemów z wydajnością, przechodząc na dysk lub przez sieć.
Mamy ten sam problem na iPadzie z dużymi obrazami. Powiadomię cię, jeśli znajdziemy jakieś rozwiązanie. –
Jednym z głównych kluczy do przewijania jest nigdy nie blokowanie głównego wątku. Głównym wątkiem jest wątek interfejsu użytkownika, który musi zarządzać aktualizacjami o wysokiej szybkości klatek na ekranie i powiązanymi z nimi zdarzeniami zwrotnymi do kontrolera. Nie widząc twojego kodu NSUURLConnection nie mogę powiedzieć, czy robisz to w oddzielnym wątku, czy nie, ale to byłby mój pierwszy przeczucie. Czy możesz opublikować swój kod obsługi sieci? – Yetanotherjosh
sprawdź projekt TithmageView github. Mają całkiem fajną implementację dla leniwych obrazów ładujących i buforowania dysku https://github.com/totocaster/TCImageView/blob/master/TCImageView.m –