【发布时间】:2010-01-13 01:34:21
【问题描述】:
我目前正在开发一个 iPhone 应用程序,该应用程序从 RSS 提要加载数据并将其显示在 2 个 UITableViews 中的标签栏应用程序中,我们称之为 TableViewA 和 TableViewB。在 AppDelegate 中我们有以下方法:
- (void)getDataWithContext:(NSManagedObjectContext *)context
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSError *parseError = nil;
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
XMLReader *xmlReader = [[[XMLReader alloc] initWithContext:context]autorelease];
[xmlReader parseXMLFileAtURL:[NSURL URLWithString:@"http://example.com/rss.xml"] parseError:&parseError];
[pool release];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
在我们的 applicationDidFinishLaunching: 方法中,我们使用该选择器调用分离一个新线程:
if ([self isDataSourceAvailable] == NO) {
UIAlertView *noConnection = [[[UIAlertView alloc] initWithTitle:@"Connection Unavailable" message:@"The connection to the database is unavailable. The information displayed may be outdated." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil] autorelease];
[noConnection show];
}
else{
[NSThread detachNewThreadSelector:@selector(getDatawithContext:) toTarget:self withObject:context];
}
TableViewControllerA 是一个 UITableViewController
使用以下方法为 TableView 加载数据和图像。当上述 XML 阅读器的线程退出时,将所有条目放入 CoreData,我们会收到通知并将条目从 CoreData 重新加载到 TableView 中。
TableViewControllerB 是一个 TableViewControllerA,它继承了这些相同的方法,并进行了一些更改以从数据库中选择不同的条目。
- (IBAction)loadData: (id) sender{
BroadwayAppDelegate *appDelegate = (BroadwayAppDelegate *) [[UIApplication sharedApplication] delegate];
checkDate = [NSPredicate predicateWithFormat: @"date <= %@",
[NSDate date]];
if ( [self.showsSegmentedControl selectedSegmentIndex] == UISegmentedControlNoSegment ||
[self.showsSegmentedControl selectedSegmentIndex] == 0){
self.listContent = [CoreDataHelper searchObjectsInContext :@"Entry" :self.checkDate :@"title" :YES :appDelegate.managedObjectContext];
}
else if ([self.showsSegmentedControl selectedSegmentIndex] == 1){
self.listContent = [CoreDataHelper searchObjectsInContext :@"Entry" :self.checkDate :@"startDate" :NO :appDelegate.managedObjectContext];
}
else if ([self.showsSegmentedControl selectedSegmentIndex] == 2){
self.listContent = [CoreDataHelper searchObjectsInContext :@"Entry" :self.checkDate :@"endDate" :YES :appDelegate.managedObjectContext];
}
else if ([self.showsSegmentedControl selectedSegmentIndex] == 3){
self.listContent = [CoreDataHelper searchObjectsInContext :@"Entry" :self.checkDate :@"type" :YES :appDelegate.managedObjectContext];
}
// create a filtered list that will contain products for the search results table.
self.filteredContent = [NSMutableArray arrayWithCapacity:[self.listContent count]];
// restore search settings if they were saved in didReceiveMemoryWarning.
if (self.savedSearchTerm)
{
[self.searchDisplayController setActive:self.searchWasActive];
[self.searchDisplayController.searchBar setSelectedScopeButtonIndex:self.savedScopeButtonIndex];
[self.searchDisplayController.searchBar setText:savedSearchTerm];
self.savedSearchTerm = nil;
}
NSError *error;
[appDelegate.managedObjectContext save:&error];
[self.tableView reloadData];
}
- (void)viewDidLoad {
[super viewDidLoad];
[self loadData:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(threadExited) name:@"NSThreadWillExitNotification" object:nil];
dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateStyle: NSDateFormatterMediumStyle];
self.title = @"Entries";
}
- (void)threadExited{
[self loadData:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self];
[NSThread detachNewThreadSelector:@selector(loadImages) toTarget:self withObject:nil];
}
- (void) loadImages{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSArray *coreDataEntries = [CoreDataHelper getObjectsFromContext:@"Entry" :@"title" :NO :appDelegate.managedObjectContext];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
for (Entry *s in coreDataEntries){
if (s.image == nil) {
NSString *URLString = [[Entry imageURLFromLink:s.link withExtension:@".jpg"]absoluteString];
NSURL *imageURL = [NSURL URLWithString:URLString];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
s.image = [UIImage imageWithData:imageData];
}
}
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[self.tableView reloadData];
[pool release];
}
如果用户在重新加载数据时或可能在 XML 阅读器退出时滚动表视图,应用程序将崩溃。为什么会发生这种情况,我们该如何解决?我们已使用提供的工具检查内存泄漏和类似情况,但没有发现任何与崩溃相关的内容。
【问题讨论】:
-
您是否尝试过使用 GDB 单步执行此代码,直到您可以观察到崩溃的发生?根据我的经验,如果您能够看到崩溃,则修复起来会非常容易,因为通常只需通过观察您的代码逐步执行的行为就可以明确根本原因。
-
另外,请记住滚动表格视图会非常频繁地调用一些表格视图委托方法:如果您在这些方法中进行任何繁重的工作,特别是如果该提升涉及共享资源,你很可能试图访问无效的内存。
-
需要日志或其他内容才能继续...而且我们甚至看不到单元格是如何制作的,这似乎更有可能是滚动时崩溃的根源。
-
我经常使用 BOOL 来确定数据是否已为 tableView 准备好,然后我在委托方法中实现 if 语句。这适用于在 tableView 开始查询行、节和单元格时无法确定数据已准备好的情况。数据准备好后,将 BOOL 设置为 YES 并调用 [tableView reloadData]。似乎您正试图同时发生多个异步事件。
-
如果你创建了自动释放池,记得
-drain。
标签: iphone objective-c uitableview iphone-sdk-3.0 scroll