您好我正在使用NSOperationQueue在后台下载图片。我创建了一个自定义的NSOperation来下载图像。我将图像放在表格单元格中。问题是如果我执行[operationQueue setMaxConcurrentOperationCount:10]并向下滚动几个单元格程序崩溃与EXC_BAD_ACCESS。每次它在桌子的同一个地方崩溃。一个接一个地有3个单元格,对于同一家公司并且具有相同的标识,所以基本上它应该下载图像3次。每隔一段时间它都能正常工作。
- (void) main
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSURL *url = [[NSURL alloc] initWithString:self.imageURL];
debugLog(@"downloading image: %@", self.imageURL);
//NSError *error = nil;
NSData *data = [[NSData alloc] initWithContentsOfURL:url];
[url release];
UIImage *image = [[UIImage alloc] initWithData:data];
[data release];
if (image)
{
if (image.size.width != ICONWIDTH && image.size.height != ICONHEIGHT)
{
UIImage *resizedImage;
CGSize itemSize = CGSizeMake(ICONWIDTH, ICONHEIGHT);
//!!! UIGraphicsBeginImageContext NOT THREAD SAFE
UIGraphicsBeginImageContext(itemSize);
CGRect imageRect = CGRectMake(0.0, 0.0, itemSize.width, itemSize.height);
[image drawInRect:imageRect];
resizedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.theImage = resizedImage;
}
else
{
self.theImage = image;
}
[image release];
}
[delegate didFinishDownloadingImage: self];
[pool release];
}
这就是我处理下载图像的方法。如果我评论出来
[delegate didFinishDownloadingImage: self];
在上面的函数中,它不会崩溃,但当然它没用。
-(void) didFinishDownloadingImage:(ImageDownloadOperation *) imageDownloader
{
[self performSelectorOnMainThread: @selector(handleDidFinishDownloadingImage:) withObject: imageDownloader waitUntilDone: FALSE];
}
-(void) handleDidFinishDownloadingImage:(ImageDownloadOperation *)imageDownloadOperation
{
NSArray *visiblePaths = [self.myTableView indexPathsForVisibleRows];
CompanyImgDownloaderState *stateObject = (CompanyImgDownloaderState *)[imageDownloadOperation stateObject];
if ([visiblePaths containsObject: stateObject.indexPath])
{
//debugLog(@"didFinishDownloadingImage %@ %@", imageDownloader.theImage);
UITableViewCell *cell = [self.myTableView cellForRowAtIndexPath: stateObject.indexPath];
UIImageView *imageView = (UIImageView *)[cell viewWithTag: 1];
if (imageDownloadOperation.theImage)
{
imageView.image = imageDownloadOperation.theImage;
stateObject.company.icon = imageDownloadOperation.theImage;
}
else
{
imageView.image = [(TestWebServiceAppDelegate *)[[UIApplication sharedApplication] delegate] getCylexIcon];
stateObject.company.icon = [(TestWebServiceAppDelegate *)[[UIApplication sharedApplication] delegate] getCylexIcon];
}
}
}
答案 0 :(得分:4)
根据此邮件列表帖子,UIGraphicsBeginImageContext
不是线程安全的。该帖子提示CGBitmapContextCreate
和相关功能是安全的方法。
答案 1 :(得分:1)
我认为你崩溃是因为你试图访问tableview中不存在的单元格。
无论逻辑表有多长,视觉表视图只保留足够的单元格来显示当前屏幕上的逻辑表的部分。在默认行大小,表格显示,因此只包含9到10个单元格对象。例如,如果您有一个100行长的逻辑表,并且您的视图显示行11-20,则表中只有9个单元格对象。如果您显示89-98行,则只有 完全相同的 9个单元格对象。无论您显示哪一行,您都会反复看到相同的9个单元格对象。唯一改变的是它们显示的数据。
如果您尝试访问屏幕上逻辑行的单元格,则不会得到任何回复。在您的情况下,您尝试访问第11个逻辑行,但没有第11个单元格,从来没有。
我认为你有一些概念混淆,因为你试图通过设置单元格内容将数据存储在tableview中。这不起作用,因为tableviews不会存储超出立即显示的数据。当tableview需要在滚动时显示更多行时,它会重用现有单元格,并且其DataSource委托会更改现有单元格显示的数据。
您需要创建数据模型并将图像存储在那里,而不是将图像存储在单元格中。然后当tableview滚动时,它会将tableview:cellForRowAtIndexPath:
发送到其数据源委托。然后,数据源委托将向数据模型询问逻辑行的数据,填充重用的单元格并将其返回到tableview。
答案 2 :(得分:0)
好吧,感谢codewarrior,它似乎已经解决了。这是改变的部分。
@synchronized(delegate) { UIImage *resizedImage; UIGraphicsBeginImageContext(itemSize); CGRect imageRect = CGRectMake(0.0, 0.0, itemSize.width, itemSize.height); [image drawInRect:imageRect]; resizedImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); self.theImage = resizedImage; }