不使用NSZombie如何确定EXC_BAD_ACCESS的根本原因?

6

非常感谢您提前给予的帮助和见解。最近我的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.

我阅读并按照这里这里这里这里的指示。当我执行“构建和运行”时,我可以100%重现exc_bad_access。然而,当我在调试器或仪器中启用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)

1
你无法获取崩溃发生的具体行,或者它变化太大,无法告诉你解决方案吗?如今,苹果似乎要求您手动添加异常断点来捕获确切的行,如果这是导致问题的原因。具体行将是弄清楚整个情况的有用线索。 - Tommy
这是太多的代码,对于任何人来说都很难在你猜测错误位置的基础上浏览。你应该像Tommy建议的一样添加一个异常断点,看看是否可以指出有问题的代码行。 - rdelmar
@Tommy,我还没有找到具体的行。当我将NSLogs放入代码中时,它总是在两个不同的位置崩溃。然而,这两个地方都在cellForRowAtIndexPath方法中返回单元格。 - Scott Lieberman
1个回答

2

看起来你在某些地方初始化时缺少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对象时,我发现无法看到设置了"EventContentGridViewCell"cellIdentifier。在以上代码中,其他自定义单元格类也是如此。但我不确定这一点是否是以上代码中唯一的问题。

如果以上更改不能解决您的问题,请查看如何向objc_exception_throw添加断点?Xcode / iPhone中未处理异常的堆栈跟踪或更多信息。这应该能帮助您找出问题发生的位置。


1
注意:将变量声明为UILabel *photoTakerLabel = nil;总是比仅声明为UILabel *photoTakerLabel;更好。这样做没有问题,但如果您错过了else部分,您可以避免崩溃。--不正确!?现在的ARC是否在初始化时将指针设置为nil...!? - Daij-Djan
缺失的标识符不应该导致崩溃,但在一个分支中我也看到了这种情况(并非所有情况都是如此)。 - Daij-Djan
1
@ACB 非常感谢!看起来你的修复解决了这个问题。我仍需要进行更多测试以验证它是否确实完全解决,但目前它不再给我之前出现的 exc_bad_access 错误。 - Scott Lieberman
1
@ScottLieberman,很高兴听到这个消息。请查阅答案中提供的另外两个链接,看看您是否能够检测到崩溃。那应该会有所帮助。 - iDev
1
对于这个答案和参考资料,给你一个赞。它们不仅对于这个特定的错误非常有帮助,而且对于任何未来的错误也很有用。谢谢。 - Scott Lieberman
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接