zoukankan      html  css  js  c++  java
  • OC开发_Storyboard——Core Data

    一 、NSManagedObjectContext

    1、我们要想操作Core Data,首先需要一个NSManagedObjectContext
    2、那我们如何获得Context呢:创建一个UIManagedDocument

    二、UIManagedDocument

    1、UIManagedDocument是一系列用于管理存储的机制:
      【将Core Data数据库放入某存储空间,相当于是管理core data 数据库的存储,所以我们只需要打开和存储】
    2、那我们如何得到UIManagedDocument呢?如何在用户文档中创建一个UIDocument?

    1 //(1文件管理器能够给我们一个用户文件目录的URL
    2 NSFileManager *fileManager = [NSFileManager defaultManager];
    3 NSURL *documentsDirectory = [[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] firstObject];
    4 
    5 //(2然后加上我们想要的文档名
    6 NSString *documentName = @“MyDocument”;
    7 NSURL *url = [documentsDirectory URLByAppendingPathComponent:documentName];
    8 //(3这个URL就是core data数据库存储的地方
    9 UIManagedDocument *document = [[UIManagedDocument alloc] initWithFileURL:url];

    3、但是我们创建的这个文档还并不存在于我们的磁盘中,还需要存储到磁盘

    1 //(1先判断是否已经存在于磁盘
    2 BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:[url path]];!
    3 //(2.1如果已经存在于磁盘,则直接打开
    4 [document openWithCompletionHandler:^(BOOL success) { /* block to execute when open */ }]; !
    5 //(2.2否则还要先存储到磁盘
    6 [document saveToURL:url forSaveOperation:UIDocumentSaveForCreating competionHandler:^(BOOL success) { /* block to execute when create is done */ }];

         e.g.下面我们看一个综合了上面的具体的例子,如何通过UIManagedDocument得到NSManagedObjectContext:

     1 self.document = [[UIManagedDocument alloc] initWithFileURL:(URL *)url];
     2 if ([[NSFileManager defaultManager] fileExistsAtPath:[url path]]) {
     3     [document openWithCompletionHandler:^(BOOL success) {
     4         if (success) [self documentIsReady];
     5         if (!success) NSLog(@“couldn’t open document at %@”, url);
     6     }];
     7 } else {
     8     [document saveToURL:url forSaveOperation:UIDocumentSaveForCreating
     9       completionHandler:^(BOOL success) {
    10           if (success) [self documentIsReady];
    11           if (!success) NSLog(@“couldn’t create document at %@”, url);
    12       }];
    13 }

         然后我们在 documentIsReady做一些操作从而获得context:

     1 - (void)documentIsReady
     2 {
     3     /*对应的状态有:
     4    UIDocumentStateClosed (还没有打开或者创建)
     5    UIDocumentStateSavingError (completion handler保存没有成功)
     6    UIDocumentStateEditingDisabled (重试)
     7    UIDocumentStateInConflict(例如有其他人在使用更新,有冲突到等)*/
     8 
     9    if (self.document.documentState == UIDocumentStateNormal) {
    10        //如果成功的话,我们就获得了我们需要的context了,然后操作core data
    11        NSManagedObjectContext *context = self.document.managedObjectContext;
    12    }
    13 }

    4、注意的点
    【但要注意的点1:上面UIManagedDocument的打开、关闭或者创建(存储)都是异步执行的】
    【需要注意的点2: UIManagedDocument是自动保存的,我们也可以调用上面的自己保存,对应的关闭也是自动关闭的】
    【需要注意的点3: UIManagedDocument是多实例的,也就是说可以多控制器同时操作,但对应的同时只有一个可写】

    5、广播站:
    比如我们在一个文档中修改了数据CoreData,但是同时 另一个并没有能马上看到,这是因为它们所用的Context不同,要想能看到需要使用NSNotiFication广播站


    三、NSNotiFication广播站

    1、如何注册一个广播

     1 - (void)viewDidAppear:(BOOL)animated
     2 {
     3    [super viewDidAppear:animated];
     4    [center addObserver:self
     5               selector:@selector(contextChanged:)
     6                   name:NSManagedObjectContextDidSaveNotification
     7                 object:document.managedObjectContext]; // don’t pass nil here!
     8 }
     9 - (void)viewWillDisappear:(BOOL)animated
    10 {
    11     [center removeObserver:self
    12                       name:NSManagedObjectContextDidSaveNotification
    13                     object:document.managedObjectContext];
    14     [super viewWillDisappear:animated];
    15 }

    2、广播得到消息之后能做什么?如何在contextChanged里操作?

    (1 可以取回我的所有对象

    1 - (void)contextChanged:(NSNotification *)notification
    2 {
    3     //  notification.userInfo 返回给我们的是一个字典包含以下的key
    4     NSInsertedObjectsKey //插入的对象数组
    5     NSUpdatedObjectsKey // 有属性更改的对象数组
    6     NSDeletedObjectsKey // 有删除的对象数组
    7 }

    (2 Merging changes:只要把notification传给它,它会自动帮我们把所有的变化合并到我们的context中
       - (void)mergeChangesFromContextDidSaveNotification:(NSNotification *)notification;

     

    四、Core Data 

    上面的操作都完备,我们就可以对我们的数据库进行增删改的操作了
    【这些操作是在内存中,不是在磁盘,但是别忘记了Document是自动保存的,所以最终还是会保存到磁盘的,只要文档保存了,Context就保存了

    1、插入

    1    NSManagedObjectContext *context = aDocument.managedObjectContext;
    2    //实体的名称:@“EntityBook”,返回NSManagedObject对象【数据库所有对象都是它或者它的子类】
    3    NSManagedObject *book = [NSEntityDescription insertNewObjectForEntityForName:@“EntityBook” inManagedObjectContext:context];
    4    //设置属性
    5    - (id)valueForKey:(NSString *)key; 
    6    - (void)setValue:(id)value forKey:(NSString *)key; 
    7    或者也可以用:valueForKeyPath:/setValue:forKeyPath

    设置属性例如:
    NSString *myThumbnail = book.thumbnailURL;
    book.lastViewedDate = [NSDate date];
    book.whoTook = ...; //这里的whoTook指的是另一张表的关联关系
    book.whoTook.name = @“CS193p Instructor”;

     

    2、删除

    //(1要注意的是这也不是马上删除,而是需要在自动保存之后才会删除,但是引用到book的地方需要在这个操作之后设置为nil
    [aDocument.managedObjectContext deleteObject:book];

    //(2 这个方法通常放在类别中,用于删除操作之后的某些更新
    - (void)prepareForDeletion{}

     

    3、查询

    (1 NSFetchRequest 提出请求从数据库请求对象
    指定要取回的实体、指定取回的对象大小数量、NSSortDescriptors排序、NSPredicate谓词哪一些数据

    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@“EntityBook”];
    request.fetchBatchSize = 20;
    request.fetchLimit = 100;
    request.sortDescriptors = @[sortDescriptor];
    request.predicate = ...;

         (2 NSSortDescriptor 排序

    1 NSSortDescriptor *sortDescriptor =
    2         [NSSortDescriptor sortDescriptorWithKey:@“title”  //排序的键
    3                                       ascending:YES       //YES是按字母排序,NO是反字母排序
    4                                        selector:@selector(localizedStandardCompare:)]; //在排序中的对比,这里的localizedStandardCompare 指代像Mac finder中的排序方式一般
    5    

    (3 NSPredicate谓词

     1     NSString *serverName = @“IOS-7”;
     2     NSPredicate *predicate =
     3     [NSPredicate predicateWithFormat:@“bookName contains %@”, serverName];
     4     
     5     @“uniqueId = %@”, [flickrInfo objectForKey:@“id”]  
     6     @“name contains[c] %@”, (NSString *) 
     7     @“viewed > %@”, (NSDate *) 
     8     @“whoTook.name = %@”, (NSString *)  
     9     @“any photos.title contains %@”, (NSString *)  
    10     @“(name = %@) OR (title = %@)”
    11     @“photos.@count > 512     @“photos.photo.title.length" 
    13     [propertyListResults valueForKeyPath:@“photos.photo.@avg.latitude”]
    14     
    15        // 复合谓词
    16     NSArray *array = @[predicate1, predicate2];
    17     NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:array];

    更多的使用可以查询:
    https://developer.apple.com/library/ios/documentation/cocoa/conceptual/KeyValueCoding/Articles/CollectionOperators.html.

           demo请求的例子:

    1     NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@“Photographer”];
    2     NSDate *yesterday = [NSDate dateWithTimeIntervalSinceNow:-24*60*60]; 
    3     request.predicate = [NSPredicate predicateWithFormat:@“any photos.uploadDate > %@”, yesterday]; 
    4     request.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@“name” ascending:YES]];
    5     
    6     NSManagedObjectContext *context = aDocument.managedObjectContext; 
    7     NSError *error;
    8     NSArray *photographers = [context executeFetchRequest:request error:&error]; 

     

    五. 生成实体的类

    1、方法
    Editor->Create NSManagedObject Subclass => 选择Model => 选择要生成的数据表EntityBook

    2、可以修改这些系统生成的类?=> 最好不要,我们可以采用类别的方法,也就是Categories
    【Categories类别可以添加方法到一个类,而不用创建它额的子类,你甚至不需要有该类的源代码】

     

    六、Categories类别

    不能使用实例变量或者任何存储数据
    1、声明

    1 @interface EntityBook (AddOn)
    2 - (NSString *)note;
    3 @property (readonly) BOOL isOld;
    4 @end

     

    2、实现

     1 @implementation EntityBook (AddOn)
     2 - (NSString *)note //要注意:note不是数据库表的属性,但是这里的bookName uploadDate是属性
     3 {
     4    NSString *bookNote = [NSString stringWithFormat:@"%@:lalalallal", self.bookName];
     5    return bookNote;
     6 }
     7 - (BOOL)isOld //  
     8 {
     9    return [self.uploadDate timeIntervalSinceNow] > -24*60*60;
    10 }
    11 @end

    3、大多数情况下的实现

     1 @implementation EntityBook (Create)
     2 
     3 + (EntityBook *)bookWithData:(NSDictionary *)Data inManagedObjectContext:(NSManagedObjectContext *)context
     4 {
     5    EntityBook *book = ...; // 查看具体某一本书是否存在
     6    if (!book) 
     7    {
     8       book = [NSEntityDescription insertNewObjectForEntityForName:@“EntityBook” inManagedObjectContext:context];
     9    }
    10    return book;
    11 }
    12 @end

    七、线程安全

    1、NSManagedObjectContext并不线程安全,任何使用在下面的BLock里面去执行 它会对Context在安全队列中执行

    [context performBlock:^{ or performBlockAndWait:
           //使用对Context做的事情,例如插入对象、查询等等
    }];

     

    八、NSFetchedResultsController 

       [使得Core Data和UITableViewController能够相辅相成。]

      1、例如:

    - (NSUInteger)numberOfSectionsInTableView:(UITableView *)sender

    {

         return [[self.fetchedResultsController sections] count];

    }

    - (NSUInteger)tableView:(UITableView *)sender numberOfRowsInSection:(NSUInteger)section

    {

         return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects];

    }

    - (UITableViewCell *)tableView:(UITableView *)sender cellForRowAtIndexPath:(NSIndexPath *)indexPath

    {

        UITableViewCell *cell = ...;

        // 或者 Book *book = (Book *) ...

        NSManagedObject *managedObject =  [self.fetchedResultsController objectAtIndexPath:indexPath];

        return cell;

    }

      2、那么如何构建一个NSFetchedResultsController呢?

    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@“EntityBook”];

    request.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@“title” ...]];

    //关联另一张表的查询,比如说作者表中的作者名称,作为我们book表的WhoTook

    request.predicate = [NSPredicate predicateWithFormat:@“whoTook.name = %@”, authorName];

    NSFetchedResultsController *frc = [[NSFetchedResultsController alloc]  

                  initWithFetchRequest:(NSFetchRequest *)request

                        managedObjectContext:(NSManagedObjectContext *)context

                        sectionNameKeyPath:(NSString *)keyThatSaysWhichSectionEachManagedObjectIsIn 

                        cacheName:@“MyPhotoCache”// 指定为nil的话,就不会执行缓存处理[永久性在磁盘中]:所以只针对总有相同FetChRequest的TableView

          ]; 

      3、让NSFetchwsResultController和TableView关联起来有两种方式,一种是利用它实现所有UITableViewDataSource的东西

       第二种就是设置委托:

    - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject

           atIndexPath:(NSIndexPath *)indexPath

         forChangeType:(NSFetchedResultsChangeType)type

          newIndexPath:(NSIndexPath *)newIndexPath

    {

        // 更新TableView[可以利用CoreDataTableViewController]

    }

      4、CoreDataTableViewController

     

     

  • 相关阅读:
    假设法求最大值和数组的优点
    要明白每个变量的语义,尽量避免一个变量有多重语义
    掷色子6000次分别统计出点子出现的次数
    c语言函数是怎么传递参数的
    为什么rand和srand总是同时出现?
    c语言解二元二次方程组
    【译】第三篇 Replication:事务复制-发布服务器
    【译】第二篇 Replication:分发服务器的作用
    【译】第一篇 Replication:复制简介
    【译】第八篇 Integration Services:高级工作流管理
  • 原文地址:https://www.cnblogs.com/daomul/p/4456240.html
Copyright © 2011-2022 走看看