天天看點

CoreData學習筆記(二)

這篇文章重點講講CoreData的Fetched Results Controller。

             對應的objc類為NSFetchedResultsController。這個類是用來管理CoreData Fetch request傳回的對象的。

             在建立這個控制器之前,必須先建立fetch request。 fetch request描述了詳細的查詢規則,還可以添加查詢結果的排序描述(sort descriptor)。fetchResultsController根據已經建立完的fetch request來建立, 它是NSFetchedResultsController的執行個體,這個執行個體的主要任務就是使用fetch request來保證它所關聯的資料的新鮮性。建立了fetchResultsController執行個體後要做一下初始化,一般初始化是向這個控制器發送PerformFetch消息,下面是這一過程的代碼。

- (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:@"Event"  
        inManagedObjectContext: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:@"timeStamp" ascending:NO];  
        NSArray *sortDescriptors = [[NSArray alloc] initWithObjects: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:managedObjectContext sectionNameKeyPath:nil  
        cacheName:@"Root"];  
        aFetchedResultsController.delegate = self;  
        self.fetchedResultsController = aFetchedResultsController;  
        [aFetchedResultsController release];  
        [fetchRequest release];  
        [sortDescriptor release];  
        [sortDescriptors release];  
        return fetchedResultsController;  
    }             

            `這個函數用來建立FetchedResultsController,過程還是比較簡單的,下面是初始化這個控制器代碼。

NSError *error = nil;  
    if(![[self  fetchedResultsController]performFetch: &error]){  
        //handle the error appropriately  
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);  
        exit(-1);  
    }             

          這段代碼一般會放在viewDidLoad函數中,初始化之後,fetchedResultsController就與資料相連接配接了,之後要取資料都能直接從這個控制器提供的方法中去取。

            實作這個控制器,最關鍵的還要實作Fetched Results Controller Delegate Methods。控制器與資料源連接配接後,控制器螢幕會時刻監視着資料源,當資料源發生

改變後,螢幕會調用對應的協定方法,改協定總共要實作四個方法,分别為:

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller;  
    - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller;  
    - (void)controller:(NSFetchedResultsController *)controller  
       didChangeObject:(id)anObject  
           atIndexPath:(NSIndexPath *)indexPath  
         forChangeType:(NSFetchedResultsChangeType)type  
          newIndexPath:(NSIndexPath *)newIndexPath;  
    - (void)controller:(NSFetchedResultsController *)controller  
      didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo  
               atIndex:(NSUInteger)sectionIndex  
         forChangeType:(NSFetchedResultsChangeType)type;             

              下面依次來解釋這四個協定方法。

              1.  - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller

            當控制器監控的資料發生改變時,如對象被删除,有插入,更新等,螢幕會在資料發生改變前意識到這個情況,此時就會調用這個函數。往往我們用清單的形式

表現資料,此時意味着螢幕上的資料即将過時,因為資料馬上要改變了,這是這個協定方法的工作就是通知清單資料馬上要更新的消息,往往代碼是這樣實作的。

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {  
    [self.tableView beginUpdates];  
}            

           2. - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller

            當fetchedResultsController完成對資料的改變時,螢幕會調用這個協定方法。在上面提到的情況,這個方法要通知清單資料已經完成,可以更新顯示的資料這個

消息,是以通常的實作是這樣的。

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {  
        [self.tableView endUpdates];  
    }             
  3. - (void)controller:(NSFetchedResultsController *)controller

              didChangeObject:(id)anObject

                        atIndexPath:(NSIndexPath *)indexPath

                  forChangeType:(NSFetchedResultsChangeType)type

                    newIndexPath:(NSIndexPath *)newIndexPath

               當fetchedResultsController發現指定的對象有改變時,螢幕會調用這個協定方法。這裡改變的類型從清單中展現有 更新、插入、删除或者行的移動。是以這個

方法要實作所有的這些方法,以應對任何一種改變。下面是這個方法的标準實作。

- (void)controller:(NSFetchedResultsController *)controller  
       didChangeObject:(id)anObject  
           atIndexPath:(NSIndexPath *)indexPath  
         forChangeType:(NSFetchedResultsChangeType)type  
          newIndexPath:(NSIndexPath *)newIndexPath {  
        switch(type) {  
            case NSFetchedResultsChangeInsert:  
                [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]   
                                withRowAnimation:UITableViewRowAnimationFade];  
                break;  
            case NSFetchedResultsChangeDelete:  
                [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]  
                                withRowAnimation:UITableViewRowAnimationFade];  
                break;  
            case NSFetchedResultsChangeUpdate: {  
                NSString *sectionKeyPath = [controller sectionNameKeyPath];  
                if (sectionKeyPath == nil)  
                    break;  
                NSManagedObject *changedObject = [controller objectAtIndexPath:indexPath];  
                NSArray *keyParts = [sectionKeyPath componentsSeparatedByString:@"."];  
                id currentKeyValue = [changedObject valueForKeyPath:sectionKeyPath];  
                for (int i = 0; i < [keyParts count] - 1; i++) {  
                    NSString *onePart = [keyParts objectAtIndex:i];  
                    changedObject = [changedObject valueForKey:onePart];  
                }  
                sectionKeyPath = [keyParts lastObject];  
                NSDictionary *committedValues = [changedObject committedValuesForKeys:nil];  
                if ([[committedValues valueForKeyPath:sectionKeyPath]isEqual:currentKeyValue])  
                    break;  
                NSUInteger tableSectionCount = [self.tableView numberOfSections];  
                NSUInteger frcSectionCount = [[controller sections] count];  
                if (tableSectionCount != frcSectionCount) {  
                    // Need to insert a section  
                    NSArray *sections = controller.sections;  
                    NSInteger newSectionLocation = -1;  
                    for (id oneSection in sections) {  
                        NSString *sectionName = [oneSection name];  
                        if ([currentKeyValue isEqual:sectionName]) {  
                            newSectionLocation = [sections indexOfObject:oneSection];  
                            break;  
                        }  
                    }  
                    if (newSectionLocation == -1)  
                        return; // uh oh  
                    if (!((newSectionLocation == 0) && (tableSectionCount == 1)  
                           && ([self.tableView numberOfRowsInSection:0] == 0)))  
                        [self.tableView insertSections:[NSIndexSet indexSetWithIndex:newSectionLocation]  
                                      withRowAnimation:UITableViewRowAnimationFade];  
                    NSUInteger indices[2] = {newSectionLocation, 0};  
                    newIndexPath = [[[NSIndexPath alloc] initWithIndexes:indiceslength:2] autorelease];  
                }  
            }  
            case NSFetchedResultsChangeMove  
                if (newIndexPath != nil) {  
                    [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]  
                                          withRowAnimation:UITableViewRowAnimationFade];  
                    [self.tableView insertRowsAtIndexPaths: [NSArray arrayWithObject:newIndexPath]  
                                          withRowAnimation: UITableViewRowAnimationRight];  
                }  
                else {  
                    [self.tableView reloadSections:[NSIndexSet  
                    indexSetWithIndex:[indexPath section]]withRowAnimation:UITableViewRowAnimationFade];  
                }  
                break;  
            default:  
                break;  
        }  
    }             

          從上面的代碼可以看出,插入,删除,移動是比較簡單的,最複雜的是更新。這個代碼是xcode的模闆代碼,基本能适用我們遇到的情況,對更新裡面的代碼我還不是非常确定,是以這裡留着等過幾天完全吃透了再補上。

           4. - (void)controller:(NSFetchedResultsController *)controller

            didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo

                                atIndex:(NSUInteger)sectionIndex

                 forChangeType:(NSFetchedResultsChangeType)type

              當改變控制器管理的對象後引起了清單section的變化,此時螢幕就會調用這個協定函數。

            下面是标準實作。

- (void)controller:(NSFetchedResultsController *)controller  
      didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo  
               atIndex:(NSUInteger)sectionIndex  
         forChangeType:(NSFetchedResultsChangeType)type {  
        switch(type) {  
            case NSFetchedResultsChangeInsert:  
                if (!((sectionIndex == 0) && ([self.tableView numberOfSections] == 1)  
                                 && ([self.tableView numberOfRowsInSection:0] == 0)))  
                    [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]  
                                  withRowAnimation:UITableViewRowAnimationFade];  
                break;  
            case NSFetchedResultsChangeDelete:  
                if (!((sectionIndex == 0) && ([self.tableView numberOfSections] == 1)  
                                 && ([self.tableView numberOfRowsInSection:0] == 0)))  
                    [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]  
                                  withRowAnimation:UITableViewRowAnimationFade];  
                break;  
            case NSFetchedResultsChangeMove:  
            case NSFetchedResultsChangeUpdate:  
            default:  
                break;  
        }  
    }