2012-01-29 42 views
3

我是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]; 
} 
+0

如果您使用保存的數據重新啓動應用程序,它是否顯示在表格視圖中?這只是一個令人耳目一新的問題? – jrturton 2012-01-29 07:19:45

+0

不,它不會在我重新啓動應用程序時顯示。它始終是空的,表格視圖中沒有顯示任何值。 – user1176103 2012-01-29 07:28:56

+0

您可以包含SQL輸出,還可以在您創建新對象的位置添加視圖控制器的代碼? – jrturton 2012-01-29 07:33:11

回答

2

從你的描述似乎與部分代碼是罪魁禍首。但是你實際上並沒有使用任何部分。因此,嘗試這種簡化:

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

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

如果仍然返回零行,請檢查您fetchedObjects.countfetchedResultsController getter方法。

+0

我試過這個,它看起來像它必須已經返回一個非在該節中的零行數,但現在在我的configureCell函數中,我得到以下未捕獲的異常: 2012-01-29 09:11:15.196 UltraTrack [18698:fb03] ***終止應用程序,由於未捕獲的異常'NSInternalInconsistencyException ',原因:'在索引0處索引1沒有對象' – user1176103 2012-01-29 17:14:58

+0

我不明白你在說什麼。上面的代碼中沒有部分。 – Mundi 2012-01-29 18:19:30

+0

提取請求中的第一個對象似乎是好的,就好像我硬編碼numberOfRowsInSection爲'return 1;',然後我看到第一個對象得到正確顯示。嘗試處理第二個對象時發生NSInternalconsistencyException。 – user1176103 2012-01-29 18:23:38

相關問題