Search code examples
xcodeuisearchbaruisearchdisplaycontrollernszombie

objectAtIndex - Message sent to deallocated instance


I am having a real problem finding where my problem is in my search controller. This is a table view with search bar and search display controller. It used to work fine, but all the sudden it stopped working. I turned on NSZombieEnabled and it shows that my NSArray called searchDataSource is the zombie.

When you type a search term the "shouldReloadTableForSearchTerm" executes the handleSearchForTerm function. The handleSearchForTerm" function accesses my ProductInfo class that query a SQLite database and returns the query results. Those results are then placed in my searchDataSource Array. Everything appears to work fine there. However, once I get to the "cellForRowAtIndexPath" function and I try to load the cells from the searchDataSource, that is when I run in to the problem of the Array having been deallocated.

Here is my code for the search controller:

//
//  SearchViewController.h
//  Priority Wire
//
//  Created by Keith Yohn on 2/2/11.
//  Copyright 2011 Priority Wire & Cable. All rights reserved.
//
#import <UIKit/UIKit.h>


@interface FourthViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, UISearchDisplayDelegate, UISearchBarDelegate> {
    UITableView *mainTableView;

    NSArray *searchDataSource;
    NSMutableArray *contentsList;
    NSMutableArray *searchResults;
    NSString *savedSearchTerm;
    NSString *webURL;
}

@property (nonatomic, retain) IBOutlet UITableView *mainTableView;
@property (nonatomic, retain) IBOutlet NSArray *searchDataSource;
@property (nonatomic, retain) NSMutableArray *contentsList;
@property (nonatomic, retain) NSMutableArray *searchResults;
@property (nonatomic, copy) NSString *savedSearchTerm;
@property (nonatomic, retain) NSString *webURL;

- (void)handleSearchForTerm:(NSString *)searchTerm;

@end

SearchViewController.m

//
//  SearchViewController.m
//  Priority Wire
//
//  Created by Keith Yohn on 2/2/11.
//  Copyright 2011 Priority Wire & Cable. All rights reserved.
//

#import "FourthViewController.h"
#import "ProductsDatabase.h"
#import "ProductInfo.h"
#import "WebViewController.h"

@implementation FourthViewController

@synthesize mainTableView;
@synthesize searchDataSource;
@synthesize contentsList;
@synthesize searchResults;
@synthesize savedSearchTerm;
@synthesize webURL;


- (void)viewDidLoad {
    [super viewDidLoad];
}

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


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [self.searchDataSource count];
}

- (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];
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    }


    // Set up the cell...
    ProductInfo *info = [searchDataSource objectAtIndex:indexPath.row]; //This is where I get the 'message sent to deallocated instance' message.
    [cell.textLabel setText:info.sName];
    [cell.detailTextLabel setText:info.sType];

    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    ProductInfo *info = [searchDataSource objectAtIndex:indexPath.row];

    webURL = [NSString stringWithFormat:@"http://www.prioritywire.com/specs/%@", info.sFile];

    WebViewController *wvController = [[WebViewController alloc] initWithNibName:@"WebViewController" bundle:[NSBundle mainBundle]];
    wvController.URL = webURL;
    wvController.navTitle = @"Spec Sheet";
    [self.navigationController pushViewController:wvController animated:YES];
    [wvController release];
}


- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];

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

- (void)viewDidUnload {
    [super viewDidUnload];

    // Save the state of the search UI so that it can be restored if the view is re-created.
    [self setSavedSearchTerm:[[[self searchDisplayController] searchBar] text]];

    [self setSearchResults:nil];
}


- (void)dealloc {
    [searchDataSource release], searchDataSource = nil;
    [mainTableView release];
    [contentsList release];
    [searchResults release];
    [savedSearchTerm release];
    [super dealloc];
}

- (void)handleSearchForTerm:(NSString *)searchTerm
{   
    [self setSavedSearchTerm:searchTerm];

    if ([self searchResults] == nil)
    {
        NSMutableArray *array = [[NSMutableArray alloc] init];
        [self setSearchResults:array];
        [array release], array = nil;
    } else {
        NSArray *productInfo = [[ProductsDatabase database] searchListing:searchTerm];
        self.searchDataSource = productInfo;
        [self.mainTableView reloadData];
        [productInfo release];
    }

    [[self searchResults] removeAllObjects];

    if ([[self savedSearchTerm] length] != 0)
    {
        for (NSString *currentString in [self contentsList])
        {
            if ([currentString rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location != NSNotFound)
            {
                [[self searchResults] addObject:currentString];
            }
        }
    }
}

#pragma mark -
#pragma mark UISearchDisplayController Delegate Methods

- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{   
    [self handleSearchForTerm:searchString];

    // Return YES to cause the search result table view to be reloaded.
    return YES;
}

- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller
{
    [self setSavedSearchTerm:nil];
    self.searchDataSource = nil;
    [self.mainTableView reloadData];
}

@end

I am quite new to objective-C and can't understand what I did wrong. I have spent days trying to figure this out and have had no luck. I would appreciate any help anyone can offer.

Keith


Solution

  • This bit of code seems to be the only place searchDataSource gets set:

        NSArray *productInfo = [[ProductsDatabase database] searchListing:searchTerm];
        self.searchDataSource = productInfo;
        [self.mainTableView reloadData];
        [productInfo release];
    

    If ProductsDatabase follows the rules, you don't own the returned array (i.e. it is already autoreleased) so the release on the fourth line is incorrect.