我是iOS新手,很抱歉,如果这是脑死的简单问题...我已经在开始之前通过一些小概念应用程序的迭代工作实施我的完整应用程序,这样它就不会像压倒一切。当我在Apple网站上的“您的第二个iOS应用程序”教程之后创建它时,我的表格视图正常工作。现在我已经尝试在标签栏应用程序中创建它,并且我看到了NSFetchedResultsController的问题,并且我不确定它是否与我在故事板或其他方面做错的事有关。即使在数据库中创建了项目,NSFetchedResultsController也不会获取结果
我有一个选项卡栏控制器,连接到嵌入在导航控制器中的表视图控制器(CatalogViewController.h/m)。表视图控制器配置为具有静态单元。在第一个静态单元格中,我有一个push segue到另一个Table View Controller(FoodCatalogViewController.h/m),它被配置为使用动态原型 - 这是我期望从我的数据库中看到对象的视图(来自Food实体 - 目前只显示名字和卡路里)。这个视图有一个“添加”按钮来在数据库中创建新的条目 - 添加按钮有一个模式继承到另一个静态表视图(AddFoodViewController.h/m),它嵌入在它自己的导航控制器中。我知道“添加”按钮正在工作,它的视图正确连接到数据库(即我正确传递/设置NSManagedObjectContext),因为如果我打开应用程序的SQLite数据库文件使用“SQLite数据库浏览器”,我看到我在模拟器中添加的项目。我只是不明白为什么他们没有通过NSFetchedResultsController显示在我的表格视图中。我使用断点检查代码,并确认performFetch代码在我的FoodCatalogViewController的fetchedResultsController函数中被调用。我在numberOfRowsInSection代码中添加了一条调试NSLog行,它似乎为零,所以我从不进入cellForRowAtIndexPath或configureCell。所以它看起来像NSFetchedResultsController是罪魁祸首 - 我只是不知道为什么它不正确地获取结果,我可以做什么来进一步调试。谁能帮我这个?
为了通过层级传递核心数据信息,我有以下代码片段:
AppDelegate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
// Setup the Catalogs Tab
UINavigationController *navigationController = [[tabBarController viewControllers] objectAtIndex:0];
CatalogViewController *catalogViewController = [[navigationController viewControllers] objectAtIndex:0];
catalogViewController.managedObjectContext = self.managedObjectContext;
return YES;
}
CatalogViewController.m(该序列中的第一个表视图控制器 - I通过传递的NSManagedObjectContext到它):
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:@"BrowseFoodCatalog"]) {
[[segue destinationViewController] setManagedObjectContext:self.managedObjectContext];
}
}
FoodCatalogViewController.h(第二个表视图控制器的序列中 - I使用NSMan agedObjectContext来设置NSFetchedResultsController):在序列中
@interface FoodCatalogViewController : UITableViewController <NSFetchedResultsControllerDelegate>
@property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;
@property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
- (void) addFoodWithName:(NSString *)name calories:(NSNumber *)calories;
@end
FoodCatalogViewController.m(第二个表视图控制器 - I使用的NSManagedObjectContext来设置NSFetchedResultsController):
@interface FoodCatalogViewController() <AddFoodViewControllerDelegate>
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath;
@end
@implementation FoodCatalogViewController
@synthesize fetchedResultsController = __fetchedResultsController;
@synthesize managedObjectContext = __managedObjectContext;
- (NSFetchedResultsController *)fetchedResultsController
{
if (__fetchedResultsController != nil) {
return __fetchedResultsController;
}
// Set up the fetched results controller.
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Food" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@"Master"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
return __fetchedResultsController;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[self.fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"FoodCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
NSManagedObject *managedObject = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = [[managedObject valueForKey:@"name"] description];
NSNumber *calorieNum = [managedObject valueForKey:@"calories"];
cell.detailTextLabel.text = [[calorieNum stringValue] description];
}
附加信息
不知道这是否相关,但为了让CoreData自动包含在我的项目中,我开始使用Single View模板,但修改了它的TemplateInfo.plist以在simi下添加以下行对于故事板LAR行:
<string>com.apple.dt.unit.coreDataCocoaTouchApplication</string>
我在别人的论坛或某事已经找到这个网上的某个地方。这是否会让CoreData产生混乱?
附加代码
按照要求,这里是我用新的元素添加到数据库的代码:
AddFoodViewController.h:
#import <UIKit/UIKit.h>
@protocol AddFoodViewControllerDelegate;
@interface AddFoodViewController : UITableViewController <UITextFieldDelegate>
@property (weak, nonatomic) IBOutlet UITextField *foodNameInput;
@property (weak, nonatomic) IBOutlet UITextField *caloriesInput;
@property (weak, nonatomic) id <AddFoodViewControllerDelegate> delegate;
- (IBAction)save:(id)sender;
- (IBAction)cancel:(id)sender;
@end
@protocol AddFoodViewControllerDelegate <NSObject>
- (void)addFoodViewControllerDidCancel:(AddFoodViewController *)controller;
- (void)addFoodViewControllerDidSave:(AddFoodViewController *)controller name:(NSString *)name calories:(NSNumber *)calories;
@end
AddFoodViewController.m:
#import "AddFoodViewController.h"
@implementation AddFoodViewController
@synthesize foodNameInput;
@synthesize caloriesInput;
@synthesize delegate = _delegate;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
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)viewDidLoad
{
[super viewDidLoad];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (void)viewDidUnload
{
[self setFoodNameInput:nil];
[self setCaloriesInput:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
if ((textField == self.foodNameInput) || (textField == self.caloriesInput)) {
[textField resignFirstResponder];
}
return YES;
}
- (IBAction)save:(id)sender {
int caloriesInt = [self.caloriesInput.text intValue];
NSNumber *caloriesNum = [NSNumber numberWithInt:caloriesInt];
[[self delegate] addFoodViewControllerDidSave:self name:self.foodNameInput.text calories:caloriesNum];
}
- (IBAction)cancel:(id)sender {
[[self delegate] addFoodViewControllerDidCancel:self];
}
@end
FoodCatalogViewController.m(AddFoodViewControllerDe使节协议代码添加到数据库中):
- (void)addFoodViewControllerDidCancel:(AddFoodViewController *)controller {
[self dismissViewControllerAnimated:YES completion:NULL];
}
- (void)addFoodViewControllerDidSave:(AddFoodViewController *)controller name:(NSString *)name calories:(NSNumber *)calories {
if ([name length]) {
[self addFoodWithName:name calories:calories];
[[self tableView] reloadData];
}
[self dismissModalViewControllerAnimated:YES];
}
- (void) addFoodWithName:(NSString *)name calories:(NSNumber *)calories {
// Create a new instance of the entity managed by the fetched results controller.
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
NSLog(@"entity name is %@", [entity name]);
NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
// If appropriate, configure the new managed object.
// Normally you should use accessor methods, but using KVC here avoids the need to add a custom class to the template.
[newManagedObject setValue:name forKey:@"name"];
[newManagedObject setValue:calories forKey:@"calories"];
CFUUIDRef uuidRef = CFUUIDCreate(NULL);
CFStringRef uuidStringRef = CFUUIDCreateString(NULL, uuidRef);
NSString* uuid = [NSString stringWithString:(__bridge NSString *)uuidStringRef];
[newManagedObject setValue:uuid forKey:@"uuid"];
// Save the context.
NSError *error = nil;
if (![context save:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
更多调试信息
奇怪 - 它看起来像fetchedResultsController没有在FoodCatalogViewController正常工作,即使managedObjectContext似乎是工作...下面是从FoodCatalogViewController.m中修改的fetchedResultsController函数和一些调试NSLog语句,并用__fetchedResultsController替换self.fetchedResultsController(因为我想知道是否导致问题)。
下面是来自fetchedResultsController功能的NSLog调用输出:
2012-01-29 10:22:21.118 UltraTrack[19294:fb03] Result: (
"<Food: 0x6e651b0> (entity: Food; id: 0x6e64630 <x-coredata://8A10B827-9F10-4760-934C-0061A982B73C/Food/p1> ; data: <fault>)",
"<Food: 0x6e653e0> (entity: Food; id: 0x6e61870 <x-coredata://8A10B827-9F10-4760-934C-0061A982B73C/Food/p3> ; data: <fault>)",
"<Food: 0x6e65450> (entity: Food; id: 0x6e64420 <x-coredata://8A10B827-9F10-4760-934C-0061A982B73C/Food/p4> ; data: <fault>)",
"<Food: 0x6e654c0> (entity: Food; id: 0x6e64430 <x-coredata://8A10B827-9F10-4760-934C-0061A982B73C/Food/p5> ; data: <fault>)",
"<Food: 0x6e65530> (entity: Food; id: 0x6e64e80 <x-coredata://8A10B827-9F10-4760-934C-0061A982B73C/Food/p2> ; data: <fault>)",
"<Food: 0x6e655b0> (entity: Food; id: 0x6e64e90 <x-coredata://8A10B827-9F10-4760-934C-0061A982B73C/Food/p6> ; data: <fault>)"
)
2012-01-29 10:22:21.907 UltraTrack[19294:fb03] Number or objects: 6
而这里的修改fetchedResultsController功能:
- (NSFetchedResultsController *)fetchedResultsController
{
if (__fetchedResultsController != nil) {
return __fetchedResultsController;
}
// Set up the fetched results controller.
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Food" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@"Master"];
aFetchedResultsController.delegate = self;
__fetchedResultsController = aFetchedResultsController;
NSArray *result = [self.managedObjectContext executeFetchRequest:fetchRequest error:nil];
NSLog(@"Result: %@", result);
NSError *error = nil;
if (![__fetchedResultsController performFetch:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
NSLog(@"Number or objects: %d", [__fetchedResultsController.fetchedObjects count]);
return __fetchedResultsController;
}
有人建议,切片的问题,所以我硬编码numberOfSectionsInTableView返回1,然后从fetchResults中的第一个对象似乎被正确处理,但我得到以下异常:
2012-01-29 10:29:27.296 UltraTrack[19370:fb03] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'no object at index 1 in section at index 0'
如果硬编码numberOfRowsInSection也返回1,那么我的数据库中的第一个对象正确显示在表视图中。对于fetchedResultsController中的info部分,有什么问题?我可以在故事板中为表格视图设置错误的部分吗?
此处,我已经试过了硬编码的2表视图功能:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
//NSLog(@"Number of sections in table view is %d", [[self.fetchedResultsController sections] count]);
//return [[self.fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(@"Number or objects: %d", [self.fetchedResultsController.fetchedObjects count]);
// If I return 1, the object is displayed correctly, if I return count, I get the exception
//return 1;
return [self.fetchedResultsController.fetchedObjects count];
// Return the number of rows in the section.
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
NSLog(@"Number of rows being returned is %d", [sectionInfo numberOfObjects]);
return [sectionInfo numberOfObjects];
}
如果您使用保存的数据重新启动应用程序,它是否显示在表格视图中?这只是一个令人耳目一新的问题? – jrturton 2012-01-29 07:19:45
不,它不会在我重新启动应用程序时显示。它始终是空的,表格视图中没有显示任何值。 – user1176103 2012-01-29 07:28:56
您可以包含SQL输出,还可以在您创建新对象的位置添加视图控制器的代码? – jrturton 2012-01-29 07:33:11