gpt4 book ai didi

ios - 对 UITableViewCell 使用 NSInvocationOperation 会导致某些 UIImageView 显示错误的图像?可能的重入问题?

转载 作者:行者123 更新时间:2023-11-28 23:11:39 26 4
gpt4 key购买 nike

我试图让我的 UITableView 在浏览大约 700 张从 Internet 下载、缓存(到内部存储)并显示在表格的每个单元格上的图片时保持平滑。就滚动性能而言,我的代码到目前为止似乎还不错。但是,我注意到有时,如果连接很糟糕或者如果我滚动得非常快,一个单元格将显示错误的图片(另一个单元格的图片)大约每秒 1/2 秒,然后更新到它应该的图像显示。

到目前为止我怀疑两件事:

一个-从我的 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;
}

最佳答案

问题是单元格的重用,一个单元格同时请求各种图像,并在下载每个图像时显示,我知道你正在取消操作队列但是由于处理调用者是同步的,操作继续执行。我建议尝试保存请求的 indexPath,并在设置 UIImage 之前将其与单元格的索引路径匹配。

关于ios - 对 UITableViewCell 使用 NSInvocationOperation 会导致某些 UIImageView 显示错误的图像?可能的重入问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8089982/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com