在我的应用程序中,我有一个表视图和一个详细信息视图。表视图是通过数据库查询填充的。我在viewDidLoad
方法中有这个数据库方法(我不知道还能把它放在哪里)然后在viewWillAppear
方法中有[self.tableView reload]
方法。问题是,当我从详细信息视图返回到表视图并单击(表视图中的)同一元素时,应用程序会崩溃。我想这是因为当我从详细视图返回并单击单元格时,数据库方法仍在运行。因此,表视图中的数据永远不会被重新加载。你们知道怎么解决这个问题吗?
*编辑:*这是数据库中的代码,我用它来填充表视图:*
- (void)viewDidLoad
{
//Convert the course id to string
NSString *selectedCategoryIdConverted = [NSString stringWithFormat:@"%d", self.selectedCategory._id];
NSMutableArray *tempArray = [[NSMutableArray alloc]init];
// Set up sqlite statement
sqlite3_stmt *dbStatement = nil;
NSString *sqlQuery = [NSString stringWithFormat:@"SELECT co.name, co.id, o.location FROM course as co JOIN categories as ca JOIN occasions as o WHERE co.catID = ca.id AND co.catID = %i AND o.courseID = co.id", self.selectedCategory._id];
//Convert the query string to const char
const char *sqlQueryConverted =[sqlQuery UTF8String];
int prepareSqlQuery = sqlite3_prepare_v2( [[DatabaseController sharedDatabaseController] getDb], sqlQueryConverted, -1, &dbStatement, NULL);
//Run the query
while ( sqlite3_step(dbStatement) == SQLITE_ROW )
{
const char *name = (const char *)sqlite3_column_text(dbStatement, 0);
int courseId = sqlite3_column_int(dbStatement, 1);
const char *location = (const char *)sqlite3_column_text(dbStatement, 2);
//Convert the returnedElement char to string
NSString *nameConverted = [[[NSString alloc] initWithUTF8String:name] autorelease];
NSString *locationConverted = [[[NSString alloc] initWithUTF8String:location] autorelease];
Course *course = [[Course alloc] initWithName:nameConverted _id:courseId location:locationConverted];
[tempArray addObject:course];
}
self.courses = tempArray;
[super viewDidLoad];
}
编辑:我注意到,当我返回到表视图时,如果我单击下一个元素,应用程序不会崩溃,但如果我单击表视图中的上一个元素(行),应用程序会崩溃。
编辑:我将代码更改为viewDidAppear
方法并修复了内存泄漏。self.courses
就是(nonatomic, retain)
这是代码:
- (void)viewWillAppear:(BOOL)animated
{
//Convert the course id to string
//NSString *selectedCategoryIdConverted = [NSString stringWithFormat:@"%d", self.selectedCategory._id];
NSMutableArray *tempArray = [[NSMutableArray alloc]init];
// Set up sqlite statement
sqlite3_stmt *dbStatement = nil;
NSString *sqlQuery = [NSString stringWithFormat:@"SELECT co.name, co.id, o.location FROM course as co JOIN categories as ca JOIN occasions as o WHERE co.catID = ca.id AND co.catID = %i AND o.courseID = co.id", self.selectedCategory._id];
//Convert the query string to const char
const char *sqlQueryConverted =[sqlQuery UTF8String];
int prepareSqlQuery = sqlite3_prepare_v2( [[DatabaseController sharedDatabaseController] getDb], sqlQueryConverted, -1, &dbStatement, NULL);
//Run the query
while ( sqlite3_step(dbStatement) == SQLITE_ROW )
{
const char *name = (const char *)sqlite3_column_text(dbStatement, 0);
int courseId = sqlite3_column_int(dbStatement, 1);
const char *location = (const char *)sqlite3_column_text(dbStatement, 2);
//Convert the returnedElement char to string
NSString *nameConverted = [[[NSString alloc] initWithUTF8String:name] autorelease];
NSString *locationConverted = [[[NSString alloc] initWithUTF8String:location] autorelease];
Course *course = [[Course alloc] initWithName:nameConverted _id:courseId location:locationConverted];
[tempArray addObject:course];
[course release];
course = nil;
}
self.courses = tempArray;
[tempArray release];
[self.tableView reloadData];
[super viewWillAppear:animated];
}
编辑:这是错误日志:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIDeviceWhiteColor _id]: unrecognized selector sent to instance 0x4d2e2a0'
Call stack at first throw:
(
0 CoreFoundation 0x010435a9 __exceptionPreprocess + 185
1 libobjc.A.dylib 0x01197313 objc_exception_throw + 44
2 CoreFoundation 0x010450bb -[NSObject(NSObject) doesNotRecognizeSelector:] + 187
3 CoreFoundation 0x00fb4966 ___forwarding___ + 966
4 CoreFoundation 0x00fb4522 _CF_forwarding_prep_0 + 50
5 BookApp 0x000079e8 -[CoursesCategoriesViewController viewWillAppear:] + 184
6 UIKit 0x0017c9be -[UINavigationController _startTransition:fromViewController:toViewController:] + 858
7 UIKit 0x0017732a -[UINavigationController _startDeferredTransitionIfNeeded] + 266
8 UIKit 0x0017e562 -[UINavigationController pushViewController:transition:forceImmediate:] + 932
9 UIKit 0x001771c4 -[UINavigationController pushViewController:animated:] + 62
10 BookApp 0x000056d6 -[CoursesViewController tableView:didSelectRowAtIndexPath:] + 374
11 UIKit 0x00135b68 -[UITableView _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:] + 1140
12 UIKit 0x0012bb05 -[UITableView _userSelectRowAtPendingSelectionIndexPath:] + 219
13 Foundation 0x0084579e __NSFireDelayedPerform + 441
14 CoreFoundation 0x010248c3 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 19
15 CoreFoundation 0x01025e74 __CFRunLoopDoTimer + 1220
16 CoreFoundation 0x00f822c9 __CFRunLoopRun + 1817
17 CoreFoundation 0x00f81840 CFRunLoopRunSpecific + 208
18 CoreFoundation 0x00f81761 CFRunLoopRunInMode + 97
19 GraphicsServices 0x012d81c4 GSEventRunModal + 217
20 GraphicsServices 0x012d8289 GSEventRun + 115
21 UIKit 0x000ccc93 UIApplicationMain + 1160
22 BookApp 0x00001ff9 main + 121
23 BookApp 0x00001f75 start + 53
)
terminate called after throwing an instance of 'NSException'
从堆栈跟踪中,有趣的部分是:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIDeviceWhiteColor _id]: unrecognized selector sent to instance 0x4d2e2a0'
和
5 BookApp 0x000079e8-[课程类别视图控制器视图Will出现:]+184
其对应于:
- (void)viewWillAppear:(BOOL)animated {
//...
NSString *sqlQuery =
[NSString stringWithFormat:
@"SELECT co.name, co.id, o.location FROM course as co JOIN categories as ca JOIN occasions as o WHERE co.catID = ca.id AND co.catID = %i AND o.courseID = co.id",
self.selectedCategory._id]; // << The culprit
//...
之所以会出现这种情况,是因为self.selectedCategory._id
试图向self.selectedCategory
发送_id
消息,但失败了,self.selectedCategory
不是好的类型。
这怎么可能呢
既然我想你肯定self.selectedCategory
是预期的类型。。。
仅仅因为self.selectedCategory
已经发布,并且它的地址已经被另一个变量重用。。。另一种类型的UIColor实例。。。可能是其他任何东西。
结论
仔细检查self.selectedCategory
属性的retain
/release
:)
旁注
+1用于FMDB
建议。。。必须!
您的代码正在泄漏内存。在将临时数组分配给self.courses后,您不会发布。此外,在将课程对象添加到临时数组后,您也不会发布课程对象。对于SQLite操作,我建议您使用FMDB。将SQL查询放在每个视图控制器中以从数据库中获取数据将非常容易管理。同样,遵循这种模式是不好的做法。将FMDB用于所有sqlite目的。为所有sqlite通信创建一个单独的类。