1. 程式人生 > >CoreData用法二:NSFetchedResultsController例項操作與講解

CoreData用法二:NSFetchedResultsController例項操作與講解

        學習了NSFetchedResultsController,才深深的體會到coredata的牛逼之處。原來Apple公司弄個新技術,不是平白無故的去弄,會給程式碼執行到來很大的好處。coredata不僅能讓我們大大的減少程式碼量,還最大化的提高執行效率。

       就拿NSFetchedResultsController來說吧,他是和UITableView搭配使用的,可以最大化的提高UITableView的UI更新效率,比如我們刪除一個東西,只需要執行刪除資料庫裡面的一條資訊,然後通過配置NSFetchedResultsController的delegate方法,它自動會找到我們刪除的那條資訊,然後自動更新UI,最重要的時它不是整體的去更新UITableView,他是隻操作了需要刪除的哪一個,這就是他的偉大之處。

      下面看看我寫的這個Demo吧

檔案結構:


將資料庫中得資料放到緩衝區中:

<span style="font-size:14px;">- (void)viewDidLoad
{
    [super viewDidLoad];
    
    NSFetchRequest * request = [[NSFetchRequest alloc] init];
    NSEntityDescription * desption = [NSEntityDescription entityForName:TABLE_NAME inManagedObjectContext:[CoreDataManage GetManagedObjectContext]];
    [request setEntity:desption];

    NSSortDescriptor * desciptor = [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:YES];
    [request setSortDescriptors:[NSArray arrayWithObjects:desciptor, nil]];
    
    //在CoreData為UITableView提供資料的時候,使用NSFetchedReslutsController能提高體驗,因為用NSFetchedReslutsController去讀資料的話,能最大效率的讀取資料庫,也方便資料變化後更新介面,
    //當我們設定好這個fetch的緩衝值的時候,我們就完成了建立 NSFetchedRequestController 並且將它傳遞給了fetch請求,但是這個方法其實還有以下幾個引數:
   // 對於managed object 內容,我們值傳遞內容。
    //sectionnamekeypath允許我們按照某種屬性來分組排列資料內容。
    //檔名的快取名字應該被用來處理任何重複的任務,比如說設定分組或者排列資料等。
    NSFetchedResultsController * resultController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:[CoreDataManage GetManagedObjectContext] sectionNameKeyPath:nil cacheName:nil];
    resultController.delegate = self;
    self.fetchController = resultController;
    NSError * error = nil;
    
    //操作我們的 fetchedResultsController 並且執行performFetch 方法來取得緩衝的第一批資料。
    if ([self.fetchController performFetch:&error])
    {
        NSLog(@"success");
       // NSLog(@"=======%@",[self.fetchController])
    }
    else
    {
        NSLog(@"error = %@",error);
    }
}
</span>

配置UITableView
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 70;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    //section配置
   // return [[self.fetchController sections] count];
    
    //row配置
    if ([[self.fetchController sections] count] > 0) {
        id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchController sections] objectAtIndex:section];
        return [sectionInfo numberOfObjects];
    }
    else
    {
        return 0;
    }
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString * mark = @"markIdentifer";
    ContentCell * cell = [tableView dequeueReusableCellWithIdentifier:mark];
    if (cell == nil)
    {
        cell = [[ContentCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:mark];
    }
    
    Student * stu = (Student *)[self.fetchController objectAtIndexPath:indexPath];
    [cell showModel:stu];
    return cell;
}


配置NSFetchedResultsController的delegate

<span style="font-size:14px;">//當資料發生變化時,點對點的更新tableview,這樣大大的提高了更新效率
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath
{
    switch (type) {
        case NSFetchedResultsChangeInsert:
            [self.contentTableView insertRowsAtIndexPaths:[NSArray arrayWithObjects:newIndexPath, nil] withRowAnimation:UITableViewRowAnimationFade];
            break;
        case NSFetchedResultsChangeDelete:
            [self.contentTableView deleteRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil] withRowAnimation:UITableViewRowAnimationFade];
            break;
        case NSFetchedResultsChangeMove:
        {
            [self.contentTableView deleteRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil] withRowAnimation:UITableViewRowAnimationFade];
            [self.contentTableView insertRowsAtIndexPaths:[NSArray arrayWithObjects:newIndexPath, nil] withRowAnimation:UITableViewRowAnimationFade];
        }
            break;
        case NSFetchedResultsChangeUpdate:
        {
            ContentCell * cell1 = (ContentCell *)[self.contentTableView cellForRowAtIndexPath:indexPath];
            Student * stu = (Student *)[controller objectAtIndexPath:indexPath];
            [cell1 showModel:stu];
        }
            break;
            
        default:
            break;
    }
}

//點對點的更新section
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
    switch(type) {
            
        case NSFetchedResultsChangeInsert:
            [self.contentTableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;
            
        case NSFetchedResultsChangeDelete:
            [self.contentTableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}

//此方法執行時,說明資料已經發生了變化,通知tableview開始更新UI
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
    [self.contentTableView beginUpdates];
}

//結束更新
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
    [self.contentTableView endUpdates];
}</span><span style="font-size:18px;">
</span>

新增一個刪除按鈕的操作,檢視效果
<span style="font-size:14px;">-(NSArray *)searchResult
{
    NSFetchRequest * request = [[NSFetchRequest alloc] init];
    NSEntityDescription * desption = [NSEntityDescription entityForName:TABLE_NAME inManagedObjectContext:[CoreDataManage GetManagedObjectContext]];
    [request setEntity:desption];
    
    NSError * error = nil;
    NSArray * result = [[CoreDataManage GetManagedObjectContext] executeFetchRequest:request error:&error];
    if (!error)
    {
        [result enumerateObjectsUsingBlock:^(Student * obj, NSUInteger idx, BOOL *stop) {
            NSLog(@"--%d,%@,%@,%@,%@--/n",idx,obj.studentnumber,obj.name,obj.age,obj.gender);
        }];
        
    }
    else
    {
        NSLog(@"error seach  = %@",error);
    }
    return result;
}


-(IBAction)delete:(id)sender
{
    NSArray * arr = [self searchResult];
    __block Student * deletemp ;
    [arr enumerateObjectsUsingBlock:^(Student * obj, NSUInteger idx, BOOL *stop) {
        if ([obj.studentnumber intValue] == 2)
        {
            deletemp = obj;
            *stop = YES;
        }
    }];
    if (deletemp)
    {
        [[CoreDataManage GetManagedObjectContext] deleteObject:deletemp];
        NSLog(@"====ok===delete");
    }
}</span><span style="font-size:18px;">
</span>

現在編譯執行你的應用的話,表面上看起來應該都是一樣的,但是如果你看看控制檯的話,驚人的事情正在發生:

SELECT 0, t0.Z_PK FROM ZFAILEDBANKINFO t0 LEFT OUTER JOIN
    ZFAILEDBANKDETAILS t1 ON t0.ZDETAILS = t1.Z_PK
    ORDER BY t1.ZCLOSEDATE DESC
total fetch execution time: 0.0033s for 234 rows.

SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZNAME, t0.ZSTATE, t0.ZCITY,
    t0.ZDETAILS FROM ZFAILEDBANKINFO t0 LEFT OUTER JOIN
    ZFAILEDBANKDETAILS t1 ON t0.ZDETAILS = t1.Z_PK WHERE
    t0.Z_PK IN  (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
    ORDER BY t1.ZCLOSEDATE DESC LIMIT 20
total fetch execution time: 0.0022s for 20 rows.

SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZNAME, t0.ZSTATE, t0.ZCITY,
    t0.ZDETAILS FROM ZFAILEDBANKINFO t0 LEFT OUTER JOIN
    ZFAILEDBANKDETAILS t1 ON t0.ZDETAILS = t1.Z_PK WHERE
    t0.Z_PK IN  (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
    ORDER BY t1.ZCLOSEDATE DESC LIMIT 20
total fetch execution time: 0.0017s for 20 rows.

你可以看到, NSFetchedResultsController 正在從 FailedBankInfo中按照之前設定的順序取得大量的ID,根據UITableView的情況每次只緩衝一定數量的資料。比我們直接操控sqlite資料庫方便多了。


原始碼下載