Question

TL:DR version: I used NSZombieEnabled to find source of EXC_BAD_ACCESS error and saw a library has 1 more release than retains. Can I assume that this library is causing the crash or can that release be associated with a retain from another library?

I am having some problems in my app with a UITableViewCell subclass instance getting messages after its retain count reaches 0. I ran the app with NSZombies and am currently trying to pair the retain/release calls to find where exactly is the error originating from. I noticed that there are only 2 retains and 3 releases with "Responsible Library" set to QuartzCore. Does it mean that that extra release call is the one that causes my app to crash? Or is it possible that a release has associated retain in another library?

Additional info: My section headers are tappable, when one is selected, the row for this section is inserted into table view and any previously visible row is deleted. In other words, there can be only 1 section at a time that has 1 row, all other sections must have 0 rows.

The release/retain calls from QuartzCore that I paired are:

CALayer layoutSublayers    (retains)
CA::Layer::layout_if_needed(CA::Transaction*)    (releases)

The release without a pair is:

CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*)

The exact line where I get the crash is the endUpdates line in:

- (void)sectionHeaderView:(SectionHeaderView *)sectionHeaderView sectionOpened:(NSInteger)sectionOpened {

    SectionInfo *sectionInfo = [self.sectionInfoArray objectAtIndex:sectionOpened];

    sectionInfo.open = YES;

    NSMutableArray *indexPathsToInsert = [[NSMutableArray alloc] init];

    [indexPathsToInsert addObject:[NSIndexPath indexPathForRow:0 inSection:sectionOpened]];


    /*
     Create an array containing the index paths of the rows to delete: These correspond to the rows for each quotation in the previously-open section, if there was one.
     */
    NSMutableArray *indexPathsToDelete = [[NSMutableArray alloc] init];

    NSInteger previousOpenSectionIndex = self.openSectionIndex;
    if (previousOpenSectionIndex != NSNotFound) {

        SectionInfo *previousOpenSection = [self.sectionInfoArray objectAtIndex:previousOpenSectionIndex];
        previousOpenSection.open = NO;
        previousOpenSection.category.model = nil;
        [previousOpenSection.headerView toggleOpenWithUserAction:NO];

        [indexPathsToDelete addObject:[NSIndexPath indexPathForRow:0 inSection:previousOpenSectionIndex]];

    }


    // Style the animation so that there's a smooth flow in either direction.
    UITableViewRowAnimation insertAnimation;
    UITableViewRowAnimation deleteAnimation;
    if (previousOpenSectionIndex == NSNotFound || sectionOpened < previousOpenSectionIndex) {
        insertAnimation = UITableViewRowAnimationTop;
        deleteAnimation = UITableViewRowAnimationBottom;
    }
    else {
        insertAnimation = UITableViewRowAnimationBottom;
        deleteAnimation = UITableViewRowAnimationTop;
    }

    NSIndexPath *indexToDelete = [indexPathsToDelete firstObject], *indexToInsert = [indexPathsToInsert firstObject];
    if (indexToDelete == nil) {
        NSLog(@"no row to delete");
    }
    else {
        NSLog(@"deleting row %d section %d", [indexToDelete row], [indexToDelete section]);
    }
    NSLog(@"inserting row %d section %d", [indexToInsert row], [indexToInsert section]);

    // Apply the updates.
    [self.tableView beginUpdates];
    [self.tableView insertRowsAtIndexPaths:indexPathsToInsert withRowAnimation:insertAnimation];
    [self.tableView deleteRowsAtIndexPaths:indexPathsToDelete withRowAnimation:deleteAnimation];
    [self.tableView endUpdates];  // this is the crash.

    self.openSectionIndex = sectionOpened;


    [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:sectionOpened] atScrollPosition:UITableViewScrollPositionNone animated:YES];

}

The error happens on iOS7.

Was it helpful?

Solution 2

I have already found a fix to this problem. It seems that one of the subviews of the table cell was calling becomeFirstResponder after an asynchronous request has ended. Of course if the cell was freed already by that moment, the crash happened. I just assumed at first that the problem is because of unbalance in retains and releases but looks like that wasn't it.

And to answer the question in title: When running with NSZombieEnabled Instruments app already pair some retain/releases when it can assume they are connected. On one of the runs of my app, Instruments joined paired that release coming from QuartzCore with a retain coming from UIKit. Even though I can't be 100% sure that it's right, I assume that "Responsible library" tags being identical is not obligatory.

TL:DR - I'm pretty sure that retain can be paired with release coming from another library.

OTHER TIPS

You can get a EXC_BAD_ACCESS at the endUpdates line if the index paths in the arrays are invalid (e.g. a section number of -1). You should NSLog your indexPathsToInsert and indexPathsToDelete and make sure they have valid values in them.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top