在 UISegment 之间快速切换时应用程序崩溃
Posted
技术标签:
【中文标题】在 UISegment 之间快速切换时应用程序崩溃【英文标题】:Application Crash on Fast switching between UISegment 【发布时间】:2013-06-20 04:12:46 【问题描述】:我有一个段和一个表视图,我必须重新加载表视图中的数据,但当我快速切换段按钮时,应用程序崩溃了。
在段上,我有三个按钮,在它们的操作上,我触发了对 url 的请求并从该 url 获取 json(3 个不同的 url 分别返回 10、17 和 8 个结果为 json)。
我有一个自定义表格单元格,其中包含图像,我正在尝试延迟加载这些图像。
在 JSON 结果中,每个 json 对象都有不同的键,例如 name、id、imageurl 等。
id 在整个应用程序中是唯一的,我正在尝试加载图像并根据该唯一 id 将图像保存在本地内存中,这样我就可以显示已兑现的图像,而无需在分段更改时重新下载图像,因为所有三个部分的一些条目都是相同的。
这是我的完整代码
- (IBAction)segmentedControlIndexChanged
NSString *offerRequestUrl = nil;
switch (self.mySegment.selectedSegmentIndex)
case 0:
offerRequestUrl = @"url_one";
self.feedRequestUrl = [NSURL URLWithString:offerRequestUrl];
break;
case 1:
offerRequestUrl = @"url_two";;
self.feedRequestUrl = [NSURL URLWithString:offerRequestUrl];
break;
case 2:
offerRequestUrl = @"url_three";;
self.feedRequestUrl = [NSURL URLWithString:offerRequestUrl];
break;
default:
break;
[self parseJSONWithURL:self.feedRequestUrl];
- (void) parseJSONWithURL:(NSURL *) jsonURL
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
NSData* data = [NSData dataWithContentsOfURL: jsonURL];
[self performSelectorOnMainThread:@selector(fetchedData:) withObject:data waitUntilDone:YES];
);
- (void)fetchedData:(NSData *)responseData
//parse out the json data
NSError* error;
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:responseData
options:kNilOptions
error:&error];
self.feedsArray = [json objectForKey:@"result"];
[self.myTableView reloadData];
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
int count = self.feedsArray.count;
// if there's no data yet, return enough rows to fill the screen
if (count == 0)
return 1;
return count;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
// customize the appearance of table view cells
//
static NSString *CellIdentifier = @"MyTableCell";
static NSString *PlaceholderCellIdentifier = @"PlaceholderCell";
// add a placeholder cell while waiting on table data
int nodeCount = [self.feedsArray count];
if (nodeCount == 0 && indexPath.row == 0)
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:PlaceholderCellIdentifier];
if (cell == nil)
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:PlaceholderCellIdentifier];
cell.detailTextLabel.textAlignment = UITextAlignmentCenter;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.detailTextLabel.text = @"Loading . . .";
[self performSelector:@selector(checkAndDisplayAlert) withObject:self afterDelay:10.0];
return cell;
CustomCell *cell = (CustomCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (nodeCount > 0)
// Configure the cell...
cell.carImageView.image = [UIImage imageNamed:@"Placeholder"]; //acts as default image
if([self.imageDict valueForKey:[NSString stringWithFormat:@"%@", self.feedsArray[indexPath.row][@"id_str"]]]==nil)
[NSThread detachNewThreadSelector:@selector(displayingSmallImage:) toTarget:self withObject:indexPath];
else
cell.carImageView.image = [self.imageDict valueForKey:[NSString stringWithFormat:@"%@", self.feedsArray[indexPath.row][@"id_str"]]];
cell.imageView.clipsToBounds = YES;
cell.titleLabel.text = self.feedsArray[indexPath.row][@"title_key"];
return cell;
- (void) displayingSmallImage:(NSIndexPath *)indexPath
NSString *imageUrl = self.feedsArray[indexPath.row][@"icon_url"];
NSURL *url = [NSURL URLWithString:imageUrl];
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
if(image == nil)
image = [UIImage imageWithContentsOfFile:[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"Placeholder"]];
[self.imageDict setObject:image forKey:[NSString stringWithFormat:@"%@", self.feedsArray[indexPath.row][@"id_str"]]];
[self performSelectorOnMainThread:@selector(imageReceived:) withObject:indexPath waitUntilDone:NO];
- (void) imageReceived:(NSIndexPath *)indexPath
UIImage *image = (UIImage *)[self.imageDict objectForKey: [NSString stringWithFormat:@"%@", self.feedsArray[indexPath.row][@"id_str"]]];
UIImageView *imgs = (UIImageView *)[[self.myTableView cellForRowAtIndexPath:indexPath] viewWithTag:IMG_TAG];
[imgs setImage:image];
[self.myTableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
- (void) checkAndDisplayAlert
if (!self.feedsArray && self.isAlertPresent == NO)
UIAlertView *nilAlert = [[UIAlertView alloc] initWithTitle:@"Sorry!"
message:@"No result returned from server."
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[nilAlert setTag:kNilAlertTag];
self.isAlertPresent = YES;
[nilAlert show];
我要做的主要事情是
懒惰下载图片
根据唯一ID缓存图片到本地内存
检查已下载的图片字典并显示它
如果可能,将这些下载的图像保存在本地内存中(nsuserdefaults 等),这样如果用户重新启动应用程序,图像也会在那里并且可以根据其唯一 ID 显示,无需再次下载。
任何帮助将不胜感激。
【问题讨论】:
也许我没有完全遵循……这四项“您必须做的主要事情”不正是您实现目标所需的吗?您是否尝试过实现其中的任何一个? 是的,我正在尝试前三名,但应用程序崩溃了。 愿意分享崩溃位置的转储吗?在旁注中,一种超级懒惰的方法是禁用 UITabBar 按钮,直到初始加载完成。这样,当您弄清楚#4时,您就可以开始了。再次......那是纯粹的创可贴。 进入主程序,现在它抛出错误 *** 由于未捕获的异常“NSRangeException”而终止应用程序,原因:“-[__NSCFArray objectAtIndex:]: index (3) beyond bounds (3)” *** 第一次抛出调用堆栈:(0x1615012 0x143ae7e 0x1614deb 0x16097e0 0x168e3a8 0xadc6 0xe85805 0xe85764 0x95b065b7 0x95af0d4e)libc++abi.dylib:终止调用抛出异常 【参考方案1】:请贴出错误日志,如果是数组越界异常我怀疑这两种方法
- (void)fetchedData:(NSData *)responseData & your tableViewDatasource
要查找导致此崩溃的代码行,请执行此操作。转到左侧的断点选项卡,然后单击底部的 +。
然后你会得到这样的东西:
刚刚完成,再次运行您的代码并告诉我们是哪一行导致了异常。
我明白你的问题了,为什么要在这个函数中从数组中取URL
- (void) displayingSmallImage:(NSIndexPath *)indexPath
您可以像这样从 cellForRowAtIndex 传递要加载到该函数的 Url:
NSArray *argsToPass=[NSArray arrayWithObjects:indexPath,self.feedsArray[indexPath.row][@"icon_url"]];
[NSThread detachNewThreadSelector:@selector(displayingSmallImage:) toTarget:self withObject:argsToPass];
你的函数被修改为:
- (void) displayingSmallImage:(NSArray *)args
if([args count] == 0)
NSString *imageUrl = [args objectAtIndex:1];
NSIndexPath *indexPath=[args objectAtIndex:0];
NSURL *url = [NSURL URLWithString:imageUrl];
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
if(image == nil)
image = [UIImage imageWithContentsOfFile:[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"Placeholder"]];
[self.imageDict setObject:image forKey:[NSString stringWithFormat:@"%@", self.feedsArray[indexPath.row][@"id_str"]]];
[self performSelectorOnMainThread:@selector(imageReceived:) withObject:indexPath waitUntilDone:NO];
【讨论】:
谢谢,我会按照这个方法,一会儿分享结果 控件在-(void)displaySmallImage:(NSIndexPath *)indexPath方法并上线[self.imageDict setObject:image forKey:[NSString stringWithFormat:@"%@", self.feedsArray[ indexPath.row][@"id_str"]]];显示 sigabert 错误 根据您的代码所说,您正在尝试从字典数组中访问字典,对吗?我想问题出在你的 self.feedsArray 上,它在传递的索引处不包含对象。 我看到您尝试延迟加载图像并且还想在本地缓存它们,请参阅我之前的回答 ***.com/questions/15865809/… 关于异步加载图像。 每次触发 Web 服务请求时,Feeds 数组都会更新,并且在所有三个 JSON 响应中很少有值/对象相同,但 id 是唯一的,并且对于所有三个来说都是相同的,并且所有文本都会正确更改但是在图像下载或显示期间它崩溃了,它只在分段之间快速切换时崩溃,平均切换速度通过分段它工作正常,但仅在快速切换时崩溃。【参考方案2】:您可以在向服务器发送请求时设置 self.view.userInteractionEnabled=NO。 当你获取数据时,只需启用用户交互。这样就可以避免多个请求。
【讨论】:
以上是关于在 UISegment 之间快速切换时应用程序崩溃的主要内容,如果未能解决你的问题,请参考以下文章