i've been dancing with a tambourine for a while, but still don't know what's the reason for that error. I've got a tableView with history of user queries data from sqlite base. I'm new to iPhone developing, so my code may be a bit excessive. The hierarchy is:
HistoryModel model-object with some init methods
HistoryDataController gets data from database and presents an array of HistoryModel objects
HistoryViewController subclass of UITableView, displays data
AppDelegate there i initially store an array of HistoryModel objects (by getting it from HistoryDataController) for HistoryViewController to access it.
The problem is, when i scroll the table or open the tab with it for the second time - it crashes with -[CFString retain]: message sent to deallocated instance
Code:
HistoryModel.h pretty unnecessary class for that case, but i want that worked to repeat in several identical cases, but a bit more complicated
@interface HistoryModel : NSObject {
int entry_id;
NSString *word;
}
- (id)initWithWord:(NSString *)word;
- (id)initWithWord:(NSString *)word andId:(int)entry_id;
@property int entry_id;
@property (retain) NSString *word;
@end
HistoryModel.m
@implementation HistoryModel
@synthesize entry_id, word;
- (id)initWithWord:(NSString *)_word {
[super init];
word = _word;
return self;
}
- (id)initWithWord:(NSString *)_word andId:(int)_entry_id {
entry_id = _entry_id;
return [self initWithWord:_word];
@end
HistoryDataController.h i use the entity of that class as getter of data and a storage for HistoryModel objects (in historyEntries property)
@interface HistoryDataController : NSObject {
NSMutableArray *historyEntries;
int limit;
}
@property (nonatomic, retain) NSMutableArray *historyEntries;
@property int limit;
- (id)initWithHistoryData;
- (id)initWithHistoryDataLimitedBy:(int)limit;
HistoryDataController.m
@implementation HistoryDataController
@synthesize historyEntries, limit;
- (id)initWithHistoryDataLimitedBy:(int)_limit {
[super init];
// Getting data from database
{some DB stuff}
NSMutableArray *tmp_historyEntries = [[NSMutableArray alloc] init];
while(result == SQLITE_ROW)
{
HistoryModel *currentHistoryEntry = [[HistoryModel alloc] initWithWord:[NSStr开发者_如何学Going stringWithUTF8String:(char *)sqlite3_column_text(statement, 1)] ];
[tmp_historyEntries addObject:currentHistoryEntry];
result = sqlite3_step(statement);
}
historyEntries = tmp_historyEntries;
{some DB stuff}
return self;
}
@end
HistoryViewController.h subclass of UITableViewController, gets data stored in AppDelegate's property and displays in the table
@interface HistoryViewController : UITableViewController {
IBOutlet UITableView *historyTable;
SynonymsAppDelegate *appDelegate;
}
@property (retain) UITableView *historyTable;
@end
HistoryViewController.m
@implementation HistoryViewController
@synthesize historyTable, historyEntriesToShow;
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
appDelegate = (SynonymsAppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate initHistoryList];
[self.tableView reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
{standart cell stuff}
HistoryModel *historyEntry = [appDelegate.historyList objectAtIndex:indexPath.row];
cell.textLabel.text = historyEntry.word;
return cell;
}
@end
SynonymsAppDelegate.h when history tab opens, it gets data of historyList property, that was formed by HistoryDataController :)
@interface SynonymsAppDelegate : NSObject <UIApplicationDelegate, UITabBarControllerDelegate> {
...
NSMutableArray *historyList;
}
...
@property (retain) NSMutableArray *historyList;
- (void)initHistoryList;
@end
SynonymsAppDelegate.m
@implementation SynonymsAppDelegate
@synthesize window, tabBarController, historyList;
- (void)initHistoryList {
HistoryDataController *historyDataController = [[HistoryDataController alloc] initWithHistoryData];
historyList = historyDataController.historyEntries;
}
@end
Fuf. Sorry for so much code, but i believe that's all necessary. As a result of half the day spent on this question, i may guess, that problem is somehow connected with HistoryModel entity, because when i delete "retain" for word @property, the error switches for -[CFString isEqualToString:]: message sent to deallocated instance
I'm not really experienced in memory management, but i guess this HistoryModel objects inside historyEntry in HistoryViewController or in historyList in AppDelegate releases somehow, when scrolling the table or opening the tab for the second time. But this's just my guessing. Really appreciate the help.
You definitely have an issue in your -[HistoryModel initWithWord] You should retain (or better yet copy) the string that is being passed.
I would write it like this:
- (id)initWithWord:(NSString *)_word {
[super init];
self.word = _word; // this is same as [self setWord:_word]
return self;
}
There are some who would say using the setter in your init is not a good practice. I'm not of that camp. But in any case, you need to be retaining or copying that string.
Then you have a similar issue in your app delegate where you are leaking each HistoryDataController as you create a new one. (and that happens every time that tableview appears). And you really should be retaining that array as well (although that hasn't caused a problem yet because you're leaking the HistoryDataControllers and therefore masking that issue so far.)
My general advice to you would be don't put off memory management. To come back later and try to get it right is complicated and error-prone even for an experienced developer. It is much, much easier to build the correct memory management techniques into the code as you write it. This means it's well worth your time to read the memory management guide first before you start coding something like this. You'll save yourself a lot of time and frustration.
精彩评论