确定没有 NSZombie 的 EXC_BAD_ACCESS 的根本原因?
Posted
技术标签:
【中文标题】确定没有 NSZombie 的 EXC_BAD_ACCESS 的根本原因?【英文标题】:Determining the root cause of an EXC_BAD_ACCESS without NSZombie? 【发布时间】:2012-10-29 04:37:37 【问题描述】:提前感谢您对我遇到的问题提供的任何帮助/见解。就在最近,我的 ios 应用程序开始因可怕的 EXC_BAD_ACCESS
错误而失败。我对 SO 和 Google 进行了大量研究,并收到了一些很好的建议,但没有一篇文章能帮助我解决我的问题。到目前为止,我知道 EXC_BAD_ACCESS 可能是以下两种情况之一:
1) A pointer is now pointing to memory that was deallocated.
2) A pointer itself is corrupt.
我阅读并遵循了here、here、here 和 here 的说明。当我执行“构建并运行”时,我可以 100% 地重现 exc_bad_access。但是,当我在调试器或 Instruments 中启用 NSZombie 时,我永远无法重现该问题。此外,NSZombie 不会在控制台中留下任何有用的信息。
我最接近查明问题的方法是在每一行代码之后放置一个 NSLog,以查看它在哪里崩溃。我相信它在两个不同的点上崩溃了——这两个点都返回了cellForRowAtIndexPath
中的一个单元格。下面我提供了两种我认为会崩溃的方法,以及控制台上的错误输出。任何帮助或指导将不胜感激,谢谢!
犯法:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
if (indexPath.section == 0)
static NSString *CellIdentifier = @"EventDetailCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.textLabel.textColor = [UIColor colorWithRed:150 green:143 blue:135 alpha:1.0];
cell.textLabel.highlightedTextColor = [UIColor whiteColor];
cell.textLabel.font = [UIFont fontWithName:@"Helvetica" size:15];
if (indexPath.row < 4)
UIImageView *disclosureArrow = [[UIImageView alloc] initWithFrame:CGRectMake(280, 15, 10, 14)];
disclosureArrow.image = [UIImage imageNamed:@"tableRowAccessoryArrow"];
[cell.contentView addSubview:disclosureArrow];
else
if ((indexPath.section == 0) && (indexPath.row == 4))
for (UIView *v in [cell.contentView subviews])
[v removeFromSuperview];
switch (indexPath.row)
case 0:
cell.textLabel.text = [NSString stringWithFormat:@"%@", self.startTime];
cell.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"eventDetailRowTop"]];
cell.imageView.image = [UIImage imageNamed:@"clockIcon"];
break;
case 1:
cell.textLabel.text = @"Location";
cell.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"eventDetailRowMiddle"]];
cell.selectedBackgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"eventDetailRowMiddleSelected"]];
cell.imageView.image = [UIImage imageNamed:@"mapIcon"];
break;
case 2:
cell.textLabel.text = [NSString stringWithFormat:@"%u attendees", [self.attendees count]];
cell.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"eventDetailRowMiddle"]];
cell.selectedBackgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"eventDetailRowMiddleSelected"]];
cell.imageView.image = [UIImage imageNamed:@"attendeesIcon"];
break;
case 3:
cell.textLabel.text = [NSString stringWithFormat:@"%u fomos", [self.fomos count]];
cell.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"eventDetailRowBottom"]];
cell.selectedBackgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"eventDetailRowMiddleSelected"]];
cell.imageView.image = [UIImage imageNamed:@"fomoIcon"];
break;
case 4:
cell.selectionStyle = UITableViewCellSelectionStyleNone;
UIImage *clockIcon = [UIImage imageNamed:@"clockIcon"];
UIImageView *iconSlot = [[UIImageView alloc] initWithFrame:CGRectMake(3, 18, 18, 18)];
iconSlot.image = clockIcon;
[cell.contentView addSubview:iconSlot];
NSString *currentTime = @"Happening Now";
UILabel *currentTimeLabel = [[UILabel alloc] initWithFrame:CGRectMake(25, 8, 150, 36)];
currentTimeLabel.backgroundColor = [UIColor clearColor];
currentTimeLabel.textColor = [UIColor whiteColor];
currentTimeLabel.font = [UIFont fontWithName:@"Helvetica" size:14];
currentTimeLabel.text = currentTime;
[cell.contentView addSubview:currentTimeLabel];
UIImage *listView = [UIImage imageNamed:@"listViewSelected"];
self.listViewButton = [UIButton buttonWithType:UIButtonTypeCustom];
self.listViewButton.frame = CGRectMake(180, 8, 36, 36);
self.listViewButton.tag = 1;
[self.listViewButton addTarget:self action:@selector(togglePhotosView:) forControlEvents:UIControlEventTouchUpInside];
[self.listViewButton setBackgroundImage:listView forState:UIControlStateNormal];
[cell.contentView addSubview:self.listViewButton];
UIImage *gridView = [UIImage imageNamed:@"gridViewIcon"];
self.gridViewButton = [UIButton buttonWithType:UIButtonTypeCustom];
self.gridViewButton.frame = CGRectMake(240, 7.2f, 36, 36);
self.gridViewButton.tag = 2;
[self.gridViewButton addTarget:self action:@selector(togglePhotosView:) forControlEvents:UIControlEventTouchUpInside];
[self.gridViewButton setBackgroundImage:gridView forState:UIControlStateNormal];
[cell.contentView addSubview:self.gridViewButton];
break;
default:
break;
return cell;
else
if ([self.listViewObjects count] == 0)
// Do something when there are no pictures or stories to show
static NSString *CellIdentifier = @"NoPhotosYetCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.textLabel.textColor = [UIColor colorWithRed:0.150 green:0.143 blue:0.135 alpha:1.0];
cell.textLabel.highlightedTextColor = [UIColor whiteColor];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.textLabel.text = @"No photos have been added yet.";
return cell;
else
if (self.isGridView == YES)
static NSString *CellIdentifier = @"EventContentGridViewCell";
EventDetailGridRow *cell = (EventDetailGridRow *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
cell = [[EventDetailGridRow alloc] initWithFrame:CGRectMake(0, 0, 306, 102)];
cell.backgroundColor = [UIColor redColor];
NSMutableArray *mutablePhotos = [NSMutableArray arrayWithArray:self.photos];
int offset = (indexPath.row * 3);
int maxRange = 3;
if (([mutablePhotos count] < (offset + maxRange)))
maxRange = ([mutablePhotos count] - offset);
NSArray *firstThree = [mutablePhotos subarrayWithRange:NSMakeRange(offset, maxRange)];
cell.photos = firstThree;
return cell;
else
static NSString *CellIdentifier = @"EventContentListViewCell";
EventDetailListRowCell *cell = (EventDetailListRowCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
cell = [[EventDetailListRowCell alloc] initWithFrame:CGRectMake(0, 0, 306, 400)];
NSString *authorName;
NSString *authorUID;
BOOL isPhoto = YES;
if ([[self.listViewObjects objectAtIndex:indexPath.row] valueForKey:@"caption"] != nil)
// It's a photo object
NSDictionary *photo = [self.listViewObjects objectAtIndex:indexPath.row];
NSString *photoID = [photo valueForKey:@"id"];
NSString *photoName = [photo valueForKey:@"image_file_name"];
NSURL *photoURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://s3.amazonaws.com/motlee-development-photos/images/%@/compressed/%@", photoID, photoName]];
authorName = [photo valueForKeyPath:@"owner.name"][0];
authorUID = [photo valueForKeyPath:@"owner.uid"][0];
UIImageView *imageContainer = [[UIImageView alloc] initWithFrame:CGRectMake(0, 20, 300, 300)];
[imageContainer setImageWithURL:photoURL];
[cell.contentView addSubview:imageContainer];
else if ([[self.listViewObjects objectAtIndex:indexPath.row] valueForKey:@"body"] != nil)
isPhoto = NO;
NSDictionary *story = [self.listViewObjects objectAtIndex:indexPath.row];
authorName = [story valueForKeyPath:@"owner.name"][0];
authorUID = [story valueForKeyPath:@"owner.uid"][0];
UIImageView *imageContainer = [[UIImageView alloc] initWithFrame:CGRectMake(0, 20, cell.frame.size.width, 148)];
imageContainer.image = [UIImage imageNamed:@"storyContainer"];
[cell.contentView addSubview:imageContainer];
UILabel *storyBody = [[UILabel alloc] initWithFrame:CGRectMake(30, 20, (306 - 60), 148)];
storyBody.numberOfLines = 3;
storyBody.backgroundColor = [UIColor clearColor];
storyBody.textAlignment = NSTextAlignmentCenter;
storyBody.font = [UIFont fontWithName:@"Helvetica" size:17];
storyBody.textColor = [UIColor whiteColor];
storyBody.text = [NSString stringWithFormat:@"\"%@\"", [story valueForKey:@"body"]];
[cell.contentView addSubview:storyBody];
else
// Something screwy going on here, should have been one of the above objects
NSURL *profilePicURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://graph.facebook.com/%@/picture", authorUID]];
UIImageView *profilePic;
if (isPhoto) profilePic = [[UIImageView alloc] initWithFrame:CGRectMake(20, 347, 40, 40)];
else profilePic = [[UIImageView alloc] initWithFrame:CGRectMake(20, 179, 40, 40)];
[profilePic setImageWithURL:profilePicURL placeholderImage:[UIImage imageNamed:@"placeholder"]];
[cell.contentView addSubview:profilePic];
UILabel *takenByLabel;
if (isPhoto)
takenByLabel = [[UILabel alloc] initWithFrame:CGRectMake(70, 342, 170, 30)];
else
takenByLabel = [[UILabel alloc] initWithFrame:CGRectMake(70, 174, 170, 30)];
takenByLabel.textColor = [UIColor lightGrayColor];
takenByLabel.font = [UIFont fontWithName:@"Helvetica" size:12];
takenByLabel.backgroundColor = [UIColor clearColor];
if (isPhoto) takenByLabel.text = @"taken by";
else takenByLabel.text = @"written by";
[cell.contentView addSubview:takenByLabel];
UILabel *photoTakerLabel;
if (isPhoto)
photoTakerLabel = [[UILabel alloc] initWithFrame:CGRectMake(70, 360, 170, 30)];
else
photoTakerLabel = [[UILabel alloc] initWithFrame:CGRectMake(70, 192, 170, 30)];
photoTakerLabel.textColor = [UIColor whiteColor];
photoTakerLabel.font = [UIFont fontWithName:@"Helvetica" size:14];
photoTakerLabel.backgroundColor = [UIColor clearColor];
photoTakerLabel.text = authorName;
[cell.contentView addSubview:photoTakerLabel];
return cell;
日志输出:
libobjc.A.dylib`objc_msgSend:
0x1f5908c: movl 8(%esp), %ecx
0x1f59090: movl 4(%esp), %eax
0x1f59094: testl %eax, %eax
0x1f59096: je 0x1f590e8 ; objc_msgSend + 92
0x1f59098: movl (%eax), %edx
0x1f5909a: pushl %edi
0x1f5909b: movl 8(%edx), %edi <-- EXC_BAD_ACCESS (code=2, address=0xb0000008)
【问题讨论】:
您无法获得崩溃发生的具体线路,或者它变化太大而无法告诉您解决方案?如今,Apple 似乎要求您手动添加一个异常断点来捕获确切的行,如果这会阻止这种情况。特定的行将有助于了解整体情况。 根据您对错误位置的预感,对于任何人来说,这都是太多的代码。您应该按照 Tommy 的建议添加一个异常断点,看看这是否会将您指向有问题的代码行。 @Tommy,我无法获得具体的线路。当我将 NSLogs 放入代码中时,它总是在两个不同的地方崩溃。但是,这两个地方都在 cellForRowAtIndexPath 方法中返回了一个单元格。 【参考方案1】:看起来您在某些地方初始化时缺少 cellIdentifier。
static NSString *CellIdentifier = @"EventContentGridViewCell";
EventDetailGridRow *cell = (EventDetailGridRow *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
cell = [[EventDetailGridRow alloc] initWithFrame:CGRectMake(0, 0, 306, 102)];//where is CellIdentifier??
应该是这样的,
cell = [[EventDetailGridRow alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.frame = CGRectMake(0, 0, 306, 102);
在为EventDetailGridRow
创建对象时,我看不到 cellIdentifier "EventContentGridViewCell"
已设置。同样的事情适用于上述代码中的所有其他自定义单元格类。但是我不确定这是否是上述代码中的问题。
如果上述更改无法解决您的问题,请查看How to add a breakpoint to objc_exception_throw? 和Stack trace or more info on unhandled exception in Xcode/iPhone。应该可以帮助您找出问题所在。
【讨论】:
:: >>注意:最好将变量声明为 UILabel *photoTakerLabel = nil;不仅仅是 UILabel *photoTakerLabel;。这没有错,但如果你错过了 else 部分,你可以避免崩溃。 ——不真实!?现在 ARC 不是在 init 上将指针设置为 nil 吗...!? 缺少的标识符不应该导致崩溃恕我直言......但我在一个 if 分支中也看到了这一点(并非在所有情况下) @ACB 非常感谢!看来您的修复解决了这个问题。我仍然需要做更多的测试来验证它确实 100% 解决了,但暂时它不再给我之前得到它的 exc_bad_access 错误。 @ScottLieberman,很高兴听到这个消息。请浏览答案中提供的其他两个链接,看看您是否可以检测到崩溃。这应该会有所帮助。 答案+1,参考文献+1。这些不仅对这个特定的错误很有帮助,而且对未来的任何错误都有帮助。谢谢。以上是关于确定没有 NSZombie 的 EXC_BAD_ACCESS 的根本原因?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Lion 下的 Xcode 4.1 Instruments 中启用 NSZombie?
如何在仪器 Xcode 3.2.5 上使用 NSZombie Enabled