Search code examples
iosuitableviewnsmutablearraynsrangeexception

Baffling crash; NSRangeException


Upon completing the loading and displaying of a UITableView which has been correctly populated, my application then crashes and gives this output:

*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 14 beyond bounds [0 .. 13]'

I have checked the contents of all the mutable arrays leading up to the tableview that could be affecting it and they all return the expected number for [mutableArray count];. The strangest part is there is not a single mutable array that could be populated [0 .. 13] since there just isn't anywhere in the entire app that has an array of 14 indexes.

The UITableView returns the lone cell as expected and only calls cellForRowAtIndexPath once (checked using NSLog). It then crashes about half a second to one second after the view appears with the NSRangeException.

The only mutable array in the UITableView class has been checked with [mutableArray count]; and returns just 1, which is the lone cell that is generated.

How do I go about debugging this when the console readout gives no indication as to what or where the array is, and the highlighted line is just a SIGBART on the autorelease in the main method?

Code from the view where the crash occurs:

header file:

#import <UIKit/UIKit.h>
#import "ActionNote.h"
#import <sqlite3.h>
#import "DBAccess.h"
#import "ActionNoteViewDetails.h"

@class ActionNoteViewDetails;


@interface ActionNoteListingViewByDates : UITableViewController<UITableViewDataSource, UITableViewDelegate>{

    NSInteger domain_id;
    NSString *url;
    NSString *selectedDate;

    NSString *dbname; 
    NSString *dbpath; 
    sqlite3 *database;
    sqlite3_stmt *selStmt; 
    sqlite3_stmt *delStmt; 
    sqlite3_stmt *insStmt; 

    NSMutableArray *items; 

    BOOL copyDb;
    NSString *graphtype;

    ActionNoteViewDetails *actionNoteViewDetails;

}

@property (nonatomic, strong) ActionNoteViewDetails *actionNoteViewDetails;

@property (nonatomic, retain) NSString *url;
@property (nonatomic, readwrite) NSInteger domain_id;
@property (nonatomic, retain) NSString *graphtype;
@property (nonatomic, retain) NSString *selectedDate;

@property (nonatomic, retain) NSMutableArray *items;

-(void) openDatabase;
-(void) readItems;

-(void) closeDatabase;
-(void) detailView:(NSIndexPath *)path;

@end

m file:

#import "ActionNoteListingViewByDates.h"


@implementation ActionNoteListingViewByDates

@synthesize domain_id;
@synthesize url;
@synthesize items;
@synthesize graphtype;
@synthesize selectedDate;

@synthesize actionNoteViewDetails;

- (void)viewDidLoad {

    [super viewDidLoad];

    items = [[NSMutableArray alloc] init]; // array for items

    copyDb = TRUE; // set copy / create flag

    // set up for app
    dbname = @"yodelaydb.sqlite3"; // database name

    // get full path of database in documents directory
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *path = [paths objectAtIndex:0];
    dbpath = [[path stringByAppendingPathComponent:dbname] retain];


    database = nil;     
    selStmt = nil;     
    delStmt = nil; 
    insStmt = nil;

    [self openDatabase]; // open database

    [self readItems];     

    self.title = selectedDate;
    self.tableView.allowsSelectionDuringEditing = true;

    [self.navigationController setToolbarHidden:NO];

}

-(void) detailView:(NSIndexPath *)path {

    UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;

    if(UIDeviceOrientationIsPortrait(deviceOrientation)){
        self.actionNoteViewDetails= [[self.storyboard instantiateViewControllerWithIdentifier:@"IDActionNoteViewDetails"] retain];

        if (path != nil) {
            ActionNote *i = (ActionNote *) [items objectAtIndex:path.row];
            NSLog(@"actionnote i %d",path.row);
            actionNoteViewDetails.note_id = i.note_id;
            actionNoteViewDetails.notetitle =i.title;
            actionNoteViewDetails.notecontent =i.content;
            actionNoteViewDetails.SelectedDate=i.notes_date;
            actionNoteViewDetails.SelectedProfile=i.domainname;
            actionNoteViewDetails.domain_id = i.domain_id;
            actionNoteViewDetails.actionmedium = i.actionMedium;
            actionNoteViewDetails.actionsource = i.actionSource;
            actionNoteViewDetails.dataFromGraph = YES;


        }
        else{
            actionNoteViewDetails.note_id = 0;
        }

        [self.navigationController pushViewController:actionNoteViewDetails animated:TRUE];

    }


    if (UIDeviceOrientationIsLandscape(deviceOrientation)){
        self.actionNoteViewDetails= [[self.storyboard instantiateViewControllerWithIdentifier:@"IDActionNoteViewDetailsLandscape"] retain];

        if (path != nil) {
            ActionNote *i = (ActionNote *) [items objectAtIndex:path.row];
            actionNoteViewDetails.note_id = i.note_id;
            actionNoteViewDetails.notetitle =i.title;
            actionNoteViewDetails.notecontent =i.content;
            actionNoteViewDetails.SelectedDate=i.notes_date;
            actionNoteViewDetails.SelectedProfile=i.domainname;
            actionNoteViewDetails.domain_id = i.domain_id;
            actionNoteViewDetails.actionmedium = i.actionMedium;
            actionNoteViewDetails.actionsource = i.actionSource;
            actionNoteViewDetails.dataFromGraph = YES;


        }
        else{
            actionNoteViewDetails.note_id = 0;
        }


        [self.navigationController pushViewController:actionNoteViewDetails animated:TRUE];


    }

}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    self.navigationItem.hidesBackButton = NO;
    [self.navigationController setToolbarHidden:YES];
    self.navigationController.navigationBarHidden=NO;

    [self.tableView reloadData];


}

-(void)dealloc{

    [actionNoteViewDetails release];
    [selectedDate release];
    [url release];
    [dbname release];
    [dbpath release];
    [items release];

    [super dealloc];
}

#pragma mark Table view methods

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

    return [items count];
}

// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle 
                                       reuseIdentifier:CellIdentifier] autorelease];
    }


    ActionNote *i = (ActionNote *) [items objectAtIndex:indexPath.row];

    NSString *displayedTitle = i.actionMedium;
    NSString *displayedContent = i.content;

    cell.textLabel.text = displayedTitle;
    cell.textLabel.font = [UIFont systemFontOfSize:15];
    cell.detailTextLabel.text = displayedContent;
    cell.detailTextLabel.font = [UIFont systemFontOfSize:11];

    if([displayedTitle isEqualToString:@"Twitter"]){
        UIImage *theImage = [UIImage imageNamed:@"bird_48_blue.png"];
        cell.imageView.image = theImage;
    }else if([displayedTitle isEqualToString:@"Blogging"]){
        UIImage *theImage = [UIImage imageNamed:@"rss_icon.png"];
        cell.imageView.image = theImage;
    }else if([displayedTitle isEqualToString:@"Affiliate Marketing"]){
        UIImage *theImage = [UIImage imageNamed:@"affiliate-marketing.png"];
        cell.imageView.image = theImage;
    }else if([displayedTitle isEqualToString:@"Online Press Release"]){
        UIImage *theImage = [UIImage imageNamed:@"press-release.png"];
        cell.imageView.image = theImage;
    }else if([displayedTitle isEqualToString:@"Public Speaking"]){
        UIImage *theImage = [UIImage imageNamed:@"public-speaking.png"];
        cell.imageView.image = theImage;
    }else if([displayedTitle isEqualToString:@"Video"]){
        UIImage *theImage = [UIImage imageNamed:@"video.png"];
        cell.imageView.image = theImage;
    }else if([displayedTitle isEqualToString:@"Email Campaign"]){
        UIImage *theImage = [UIImage imageNamed:@"email.png"];
        cell.imageView.image = theImage;
    }else if([displayedTitle isEqualToString:@"Offline Press Release"]){
        UIImage *theImage = [UIImage imageNamed:@"press-release.png"];
        cell.imageView.image = theImage;
    }else{
        UIImage *theImage = [UIImage imageNamed:@"action-note.png"];
        cell.imageView.image = theImage;
    }

    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;

    return cell;
}

// Override to support row selection in the table view.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {   
    // get the item for this cell

        [self detailView:indexPath];

}


-(void)openDatabase {

    BOOL ok;
    NSError *error;


    NSFileManager *fm = [NSFileManager defaultManager]; // file manager
    ok = [fm fileExistsAtPath:dbpath];

    // if database not there, copy from resource to path
    if (!ok)
    {
        if (copyDb)
        { // copy the database
            // location in resource bundle
            NSString *appPath = [[[NSBundle mainBundle] resourcePath] 
                                 stringByAppendingPathComponent:dbname];
            // copy from resource to where it should be
            ok = [fm copyItemAtPath:appPath toPath:dbpath error:&error];
        }
    }
    //[fm release];

    // open database
    if (sqlite3_open([dbpath UTF8String], &database) != SQLITE_OK)
    {
        sqlite3_close(database); // in case partially opened
        database = nil; // signal open error
    }

    if (!copyDb && !ok)
    { // first time and database not copied
        ok = TRUE;//[self createDatabase]; // create empty database
    }

    if (!ok)
    { // problems creating database
        //NSAssert1(0, @"Problem creating database [%@]", [error localizedDescription]);
    }

}

-(void)readItems {

    if (!database) return; // earlier problems

    // build select statement
    if (!selStmt)
    {
        const char *sql = "SELECT note_id,title,settingid,domain_id,content,notes_date,domainname,actionSource,actionMedium,iscompleted FROM notes where iscompleted=1 and notes_date=? order by notes_date";
        if (sqlite3_prepare_v2(database, sql, -1, &selStmt, NULL) != SQLITE_OK)
        {
            selStmt = nil;
        }

        sqlite3_bind_text(selStmt, 1, [selectedDate UTF8String] , -1, SQLITE_TRANSIENT);
    }
    if (!selStmt)
    {
        NSAssert1(0, @"Can't build SQL to read notes [%s]", sqlite3_errmsg(database));
    }


    int ret;
    while ((ret=sqlite3_step(selStmt))==SQLITE_ROW) 
    { // get the fields from the record set and assign to item
        // primary key

        NSInteger n = sqlite3_column_int(selStmt, 0); 
        ActionNote *item = [[ActionNote alloc] initWithPrimaryKey:n]; // create item
        // item name
        char *s = (char *)sqlite3_column_text(selStmt, 1);
        if (s==NULL) s = "";
        item.title = [NSString stringWithUTF8String:(char *)s];


        NSInteger settingid = sqlite3_column_int(selStmt, 2);
        item.settingid = settingid;

        NSInteger domainid = sqlite3_column_int(selStmt, 3);
        item.domain_id = domainid;


        // item name
        char *content = (char *)sqlite3_column_text(selStmt, 4);
        if (content==NULL) content = "";
        item.content = [NSString stringWithUTF8String:(char *)content];

        // item name
        char *notes_date = (char *)sqlite3_column_text(selStmt, 5);
        if (notes_date==NULL) notes_date = "";
        item.notes_date = [NSString stringWithUTF8String:(char *)notes_date];

        // item name
        char *domainname = (char *)sqlite3_column_text(selStmt, 6);
        if (domainname==NULL) domainname = "";
        item.domainname = [NSString stringWithUTF8String:(char *)domainname];
       // NSLog(@"domain name %@",item.domainname);
        // item name
        char *actionSource = (char *)sqlite3_column_text(selStmt, 7);
        if (actionSource==NULL) actionSource = "";
        item.actionSource = [NSString stringWithUTF8String:(char *)actionSource];
       // NSLog(@"actionSource %@",item.actionSource);
        // item name
        char *actionMedium = (char *)sqlite3_column_text(selStmt, 8);
        if (actionMedium==NULL) actionMedium = "";
        item.actionMedium = [NSString stringWithUTF8String:(char *)actionMedium];
       // NSLog(@"actionMedium %@",item.actionMedium);
        // item name
        NSInteger completed = sqlite3_column_int(selStmt, 9);
        item.iscompleted = completed;



        [items addObject:item]; // add to list

        [item release]; // free item
    }
    sqlite3_reset(selStmt); // reset (unbind) statement

    [self closeDatabase];
}

-(void)closeDatabase {
    sqlite3_finalize(selStmt); // release memory
    sqlite3_close(database);
}

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)didReceiveMemoryWarning
{

    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];

    // Release any cached data, images, etc that aren't in use.
}

#pragma mark - View lifecycle

- (void)viewDidUnload
{
    [super viewDidUnload];
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orientation
{
    return YES;
}
@end

Solution

  • It ended up being a rather confusing bug in a version of a framework that was being used in the class that was handling the data in the previous view.