• CoreData (三)备


    NSFetchedResultsController

    什么是NSFetchedResultsController

    NSFetchedResultsController是一个让人爱恨交加的一个类。如果使用得当,NSFetchedResultsController能帮组减少很多代码。如果使用不当,整个App就随时崩溃。

    NSFetchedResultsController我觉得最初的设计应该是为了配合UITableView来使用的。因为UITableView在iOS的应用App中出场次数实在是太高了.而且UITableView是重要的数据展示View,所以需要频繁的向Model去请求数据,但是根据MVC来说,V不应该直接跟M联系的.这样就在Core Data下面出现了一个C—NSFetchedResultsController来把V和M协调起来. NSFetchedResultsController就是这个C.

    NSFetchedResultsController是有两个重要的功能。

    第一:NSFetchedResultsController是作用在Core Data上的,通过NSFetchRequest来查询Core Data里面的数据.可以返回按照组分好的数据.这样便于UITableView来显示.

    第二:但Modle改变的时候NSFetchedResultsController能及时的发出通知.准确的说,应该是当NSManagedObjectContext发生改变的时候,NSFetchedResultsController能知道这些变化,然后发出通知出来.以便UITableview能及时的更新.

    实现一个NSFetchedResultsController作为Data source的UITableView

    创建一个最小带Core Data的工程

    选择Master-Detail Application

    整理一下显示层级和结构使其看起来顺眼一些

    确立目标

    打开看以后 发现建立的工程是已经使用了NSFetchedResultsController
    我们的目标是改写这个项目支持UITableView分组显示

    首先修改Data Model

    增加一个字段用来分组. 我们增加一个同样的Date用来记录此时的分钟数量.

    初始化一个NSFetchedResultsController

    这里假设你看过我的Core Data笔记1,2.默认你已经做好了Core Data stack的全部工作.然后再开始NSFetchedResultsController的初始化.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    - (NSFetchedResultsController *)fetchedResultsController
    {    if (_fetchedResultsController != nil) {        
            return _fetchedResultsController;
        }
     
        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
        // Edit the entity name as appropriate.
        NSEntityDescription *entity = [NSEntityDescription entityForName:@"Event" 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:@"timeStamp" ascending:NO];
        NSArray *sortDescriptors = @[sortDescriptor];
     
        [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:@"sectionMinute" cacheName:@"Master"];
        aFetchedResultsController.delegate = self;
        self.fetchedResultsController = aFetchedResultsController;
     
        NSError *error = nil;    
        if (![self.fetchedResultsController performFetch:&error]) {
            // Replace this implementation with code to handle the error appropriately.
            // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
        return _fetchedResultsController;
    }

    这里是工程里面的NSFetchedResultsController的set方法.可以看出,第一我们创建一个NSFetchRequest查询.然后在用这个NSFetchRequest去创建一个NSFetchedResultsController.

    1
    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"sectionMinute" cacheName:@"Master"];

    第一个参数就是NSFetchRequest.

    第二个参数是要指定在哪个context里面进行查询

    第三个参数是根据什么key来分组.sectionNameKeyPath本来是nil是不分组,我改为我们需要分组的key值“sectionMinute”.

    第四个参数 官方解释是这里点到The Cache的地方. 我的理解是cache只保留很少的一部分数据在磁盘上面,如果使用了Cache,在重建UITableView的时候, 就优先查询cache里面的数据.然后要在-performFetch:执行的时候才会去刷新新的数据.这样有助于UITableView的流畅性.

    然后我加入Sections的方法

    1
    2
    3
    4
    5
    6
    7
    - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
    {
        NSArray *sections = [[self fetchedResultsController] sections]; id <NSFetchedResultsSectionInfo> sectionInfo = nil;
        sectionInfo = [sections objectAtIndex:section];
         
        return [sectionInfo name];
    }

    接着我添加

    1
    https://github.com/erica/NSDate-Extensions.git

    这个NSDate库进来.自己按照他的写法,写一个能获得当前时间秒数为0的方法.用来分组.

    1
    2
    3
    4
    5
    - (NSDate *) dateAtStartOfMinutes
    {    NSDateComponents *components = [CURRENT_CALENDAR components:DATE_COMPONENTS fromDate:self];
        [components setSecond:0];
        return [CURRENT_CALENDAR dateFromComponents:components];
    }

    然后改写insert方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    - (void)insertNewObject:(id)sender
    {
        NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
        NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
        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:[NSDate date] forKey:@"timeStamp"];
        [newManagedObject setValue:[[NSDate date] dateAtStartOfMinutes]  forKey:@"sectionMinute"];
         
        // Save the context.
        NSError *error = nil;    if (![context save:&error]) {
             // Replace this implementation with code to handle the error appropriately.
             // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
    }

    其实就是加入了

    1
    [newManagedObject setValue:[[NSDate date] dateAtStartOfMinutes]  forKey:@"sectionMinute"];

    这句.

    这样,简单的使用NSFetchedResultsController来显示分组的UITableView就搞定了.
    当然因为建立的工程模板原因.很大一部分都是xCode搞定的.

    被遗忘的地方

    Sections数量,决定了有多少组

    1
    2
    3
    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
    {    return [[self.fetchedResultsController sections] count];
    }

    如果在初始化NSFetchedResultsController的时候sectionNameKeyPath为nil.这里应该会返回1.(就算没有数据也会返回1)

    Row数量,决定每一组分别有多少行数据.

    1
    2
    3
    4
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {    id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];    
        return [sectionInfo numberOfObjects];
    }

    我前面说过NSFetchedResultsController就是为了配合UITableView而设计的.所以自然有根据indexPath来取对应的NSManagedObject的方法.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {    
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
        [self configureCell:cell atIndexPath:indexPath];    
        return cell;
    }
     
    - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
    {
        NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
        cell.textLabel.text = [[object valueForKey:@"timeStamp"] description];
    }

    show

    总结

    写Blog实在是太累了. NSFetchedResultsController努力一天也才一点点.回去继续写. 下次要写NSFetchedResultsController通知方法.

  • 相关阅读:
    Jmeter响应断言的处理。
    Jmeter超时处理。
    HTTP协议简介以及特点。
    自动化测试面试技巧。
    父类构造方法有无参数对子类的影响。
    自动化分层思想分析1.
    设计模式
    遍历课程列表代码。
    如何遍历当前页课程定位分析,以及代码编写。
    “笨方法”学习Python笔记(1)-Windows下的准备
  • 原文地址:https://www.cnblogs.com/isItOk/p/5357358.html
Copyright © 2020-2023  润新知