Я пытаюсь обеспечить плавную прокрутку UITableView
, просматривая около 700 изображений, которые загружаются из Интернета, кэшируются (во внутреннее хранилище) и отображаются в каждой ячейке таблицы.Мой код пока выглядит хорошо, насколько прокручивается производительность.Тем не менее, я заметил, что иногда, если соединение прерывается или если я прокручиваю очень быстро, ячейка будет отображать неправильное изображение (изображение другой ячейки) в течение примерно 1/2 секунды, а затем обновлять изображение, которое предполагается
На данный момент я подозреваю 2 вещи:
A- У меня может возникнуть проблема с повторным входом с того момента, когда мои NSInvocationOperation
перезванивают в основной поток с [self performSelectorOnMainThread:]
наТочка, в которой выполняется селектор в основном потоке.Хотя на самом деле я не вижу общих переменных.
B - Какая-то гонка между основным потоком и NSInvocationOperation
?Например:
1 вызов основного потока cacheImageFromURL
2 внутри этого вызова UIImage
охватывает рабочий поток
3 рабочий поток почти завершен и получает вызов performSelectorOnMainThread
4 соответствующая ячейка исключена для повторного использования в этой точке, поэтому основной поток снова вызывает cahceImageFromURL
для нового изображения.
5 внутри этого вызова UIImage останавливает NSOPerationQueue
, что приводит к смерти предыдущего потока NSInvocationOperation
.
6 НО, поток уже вызвал performSelectorOnMainThread
7, поэтому селектор возбуждается, вызывая загрузку старого изображения.
8 сразу после этого, недавно появившийсяпоток завершает выборку нового изображения и снова вызывает performSelectorOnMainThread
, вызывая обновление правого изображения.
Если это так, то мне нужно установить флаг при входе в cacheImageFromURL
метод, чтобы код рабочего потока не вызывал performSelectorOnMainThread
, если внутри cacheImageFromURL
?
уже есть другой поток (основной). Вот мой код для моего UIImageView
подкласса, каждая ячейка которого находится втаблица использует:
@implementation UIImageSmartView
//----------------------------------------------------------------------------------------------------------------------
@synthesize defaultNotFoundImagePath;
//----------------------------------------------------------------------------------------------------------------------
#pragma mark - init
//----------------------------------------------------------------------------------------------------------------------
- (void)dealloc
{
if(!opQueue)
{
[opQueue cancelAllOperations];
[opQueue release];
}
[super dealloc];
}
//----------------------------------------------------------------------------------------------------------------------
#pragma mark - functionality
//----------------------------------------------------------------------------------------------------------------------
- (bool)cacheImageFromURL:(NSString*)imageURL
{
/* If using for the first time, create the thread queue and keep it
around until the object goes out of scope*/
if(!opQueue)
opQueue = [[NSOperationQueue alloc] init];
else
[opQueue cancelAllOperations];
NSString *imageName = [[imageURL pathComponents] lastObject];
NSString* cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *cachedImagePath = [cachePath stringByAppendingPathComponent:imageName];
/* If the image is already cached, load it from the local cache dir.
Else span a thread and go get it from the internets.*/
if([[NSFileManager defaultManager] fileExistsAtPath:cachedImagePath])
[self setImage:[UIImage imageWithContentsOfFile:cachedImagePath]];
else
{
[self setImage:[UIImage imageWithContentsOfFile:self.defaultNotFoundImagePath]];
NSMutableArray *payload = [NSMutableArray arrayWithObjects:imageURL, cachedImagePath, nil];
/* Dispatch thread*/
concurrentOp = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(loadURI:) object:payload];
[opQueue addOperation: concurrentOp];
[concurrentOp release];
}
return YES;
}
//----------------------------------------------------------------------------------------------------------------------
/* Thread code*/
-(void)loadURI:(id)package
{
NSArray *payload = (NSArray*)package;
NSString *imageURL = [payload objectAtIndex:0];
NSString *cachedImagePath = [payload objectAtIndex:2];
/* Try fetching the image from the internets.
If we got it, write it to disk. If fail, set the path to the not found again.*/
UIImage *newThumbnail = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imageURL]]];
if(!newThumbnail)
cachedImagePath = defaultNotFoundImagePath;
else
[UIImagePNGRepresentation(newThumbnail) writeToFile:cachedImagePath atomically:YES];
/* Call to the main thread - load the image from the cache directory
at this point it'll be the recently downloaded one or the NOT FOUND one.*/
[self performSelectorOnMainThread:@selector(updateImage:) withObject:cachedImagePath waitUntilDone:NO];
}
//----------------------------------------------------------------------------------------------------------------------
- (void)updateImage:(NSString*)cachedImagePath
{
[self setImage:[UIImage imageWithContentsOfFile:cachedImagePath]];
}
//----------------------------------------------------------------------------------------------------------------------
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
@end
И способ использования этого UIImage находится в контексте cellForRowAtIndexPath, например:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UIImageSmartView *cachedImage;
// and some other stuff...
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.selectionStyle = UITableViewCellSelectionStyleGray;
// some labels and tags stuff..
cachedImage = [[UIImageSmartView alloc] initWithFrame:CGRectMake(5, 5, 57, 80)];
cachedImage.contentMode = UIViewContentModeCenter;
cachedImage.defaultNotFoundImagePath = [[NSBundle mainBundle] pathForResource:@"DefaultNotFound" ofType:@"png"];
cachedImage.tag = PHOTO_TAG;
[cell.contentView addSubview:cachedImage];
[cell.contentView addSubview:mainLabel];
[cell.contentView addSubview:secondLabel];
}
else
{
cachedImage = (UIImageSmartView*)[cell.contentView viewWithTag:PHOTO_TAG];
mainLabel = (UILabel*)[cell.contentView viewWithTag:MAINLABEL_TAG];
}
// Configure the cell...
NSString *ImageName = [[[self.dbData objectAtIndex:indexPath.row] objectAtIndex:2]
stringByReplacingOccurrencesOfString:@".jpg"
withString:@"@57X80.png"];
NSString *imageURL = [NSString stringWithFormat:@"www.aServerAddress.com/%@/thumbnail5780/%@",
self.referencingTable,
ImageName];
[cachedImage cacheImageFromURL:imageURL];
mainLabel.text = [[self.dbData objectAtIndex:indexPath.row] objectAtIndex:0];
return cell;
}