- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我试图让我的 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/
我在操作里面取消一个NSInvocationOperation是不是对的?示例: .h文件: //defined in the interface NSInvocationOperation *op1
我知道类似的问题已被问过几次,但我正在努力了解如何解决这个特定问题。到目前为止,我所做的一切都是在主踏板上进行的。我现在发现我需要执行一个需要一些时间的操作,我想在操作期间向我的显示器添加一个 HUD
- (void)viewDidLoad { NSOperationQueue *operationQueue = [[NSOperationQueue alloc]init];
我已经在我的项目中编写了代码 [NSInvocationOperation alloc] initWithTarget:
我正在尝试使用 NSInvocationOperation 对一些 TWRequest 调用进行排队。它似乎以正确的顺序添加方法调用,但 doSomething: 方法几乎同时被调用,即并发运行,而不
我正在尝试创建 NSInvocationOperation 以便它应该使用参数调用对象的方法 - (void) getImages: (NSRange) bounds { NSOperatio
我有一个 NSInvocationOperation,它会在后台下载和解析一系列 NSXMLDocuments 以响应我的 UI。 我尝试停止调用操作是调用我的 NSOperationQueue 的
Objective-C/iOS 领域的新手。拥有 PHP 和 C++ 经验。 我在使用 NSInvocationOperation 时遇到了一些问题。这是我的情况: 借助一些在线帮助,我有一个类 ge
1) 当我们在 NSoperationQueue 中添加 NSOperation 或 NSInvocationOperation 时发生了什么? 2) NSoperation 和 NSInvocati
我正在将我制作的 Android 应用移植到 iOS。 Android 有一个 Yield() 函数可以将线程从运行中移到线程队列的后面(?)。这很有用,这样该线程就不会占用过多的 CPU 并使其他一
Foundation Framework 中共有三个操作类(NSOperation、NSInvocationOperation 和NSBlockOperation)。 我已经阅读了 concurren
我正在尝试将 NSOperation 对象放入 NSOperationQueue 中。但似乎我误解了如何正确初始化 NSInvocationOperation(一个似乎为我的目的量身定制的子类,因为我
我在 iOS5 中使用 NSInvocationOperation 和 NSOperationQueue 开发。根据关于调用对象的苹果文档: The NSInvocationOperation cla
我有一个相当简单但昂贵的任务需要在后台运行,这是标准的 NSOperation 情况。我还需要确保该操作支持取消并适当停止。鉴于该要求,哪种方法更好:只是将昂贵的方法调用包装在 NSInvocatio
我的代码突然无法在 Xcode 6.1 中编译(我确定它在 Xcode 6 GM 和 beta 版本中工作)。它显示错误消息: 'NSInvocationOperation' is unavailab
我试图让我的 UITableView 在浏览大约 700 张从 Internet 下载、缓存(到内部存储)并显示在表格的每个单元格上的图片时保持平滑。就滚动性能而言,我的代码到目前为止似乎还不错。但是
我是一名优秀的程序员,十分优秀!