• ios之coredata(二)


    上面一篇文章介绍了coredata的有关基本概念,由于大部分是参考别人文章中的内容,所以感觉有点虚,而且估计也是比较难以理解,下面这篇文章通俗一点说说学习coredata后的一些理解,然后给出一个简单的demo,有错漏的地方,欢迎读者指正。

    其实与coredata有关的有几个概念:

     

     

    • 数据表 --–> Entity  (You usually define entities in a managed object model usingthe data modeling tool in Xcode).
    • 表格的记录 --> NSManagedObject (一个表记录就是一个NSManagedObject实例)
    • 描述表格结构 --> NSEntityDescription
    • 数据库中所有表格和他们的联系 -->NSManagedObjectModel
    • 数据库存放方式 --> NSPersistentStoreCoordinator
    • 数据库操作 --> NSManagedObjectContext
    • 查询语句 --> NSFetchRequest

     

    下面依次讲解一下:

    (1)Entity,实体。这个是数据表,也就是你在xcode中可视化编辑的那个东西。它含有Attributes,Relationship和Fetched Property三个属性,其中叫常用的是前两个属性。如下图,就是包含了两个实体(DeviceItem和ImageData),每个实体都含有Attributes,Relationship。

    所以可以这样理解,实体就是一个抽象的数据表

     

    (2)Managed Object,托管对象。是对于Entity实体的模型文件,它有一个基类NSManagedObject,所以的模型文件都是继承NSManagedObject而来。那么我们对应上面说到的两个实体,就可以对应的创建两个模型文件。如下图:


    这些模型文件其实就是继承NSManagedObjectd的类那么以后我们要添加一条记录或者删除一个记录,那么操作的对象就是这些类的实例对象,也就是说一个记录就是一个NSManagedObject实例。

    注意到在这些文件中对属性使用的是@dynamic修饰,如在DeviceItem.m文件中

     

    1. @dynamic iD;  
    2. @dynamic imageURL;  
    3. @dynamic link;  
    4. @dynamic title;  
    5. @dynamic image;  


    那么@dynamic和@synthesize有什么区别

     

     

    在声明property属性后,有2种实现选择

    @synthesize

    编译器期间,让编译器自动生成getter/setter方法。

    当有自定义的存或取方法时,自定义会屏蔽自动生成该方法

     

    @dynamic

    告诉编译器,不自动生成getter/setter方法,避免编译期间产生警告

    然后由自己实现存取方法或存取方法在运行时动态创建绑定:主要使用在CoreData的实现NSManagedObject子类时使用,由Core Data框架在程序运行的时动态生成子类属性

     
    (3)NSEntityDescription,实体描述显然这个就是用来描述实体的,它包含的内容就包括实体的属性,关系等信息,我们通过NSEntityDescription就可以了解实体,那么通过这个NSEntityDescription,我们就可以创建对应实体的Managed Object。所以说,它是用来描述表的结构的。

     

     

    (4)NSManagedObjectModel,托管对象模型。模型由多个实体描述对象Entity Description构成。通过下图就可以知道什么是托管对象模型了。


     

    (5)NSPersistentStoreCoordinator,持久化存储管理的助理,它是用来负责数据存储的方式的,我们知道coredata的数据存储方式可以有多种SQLite3数据库、二进制文件、XML文件,那么它就是负责这部分内容的。

     

    (6)NSManagedObjectContext,简称是上下文。通过下面这张图应该可以了解上下文的作用了,所以的Managered Object都要在上下文中登记注册,那么你对Managered Object进行操作后,通过上下文就可以进行保存修改,也就说通过上下文进行对数据库数据的以一些操作。


     

    (7)NSFetchRequest ,获取数据的请求,我们要想从上下文中获取到数据,首先还要创建数据请求。创建请求过程中可以指定一些参数,首先是实体,每次获取数据只能指定一种实体,也即指定一种数据表类型,这时候要传入的参数当然就是Entity Description啦;其次还要指定NSPredicate,谓词,也就是限定条件,只有满足条件的数据才被返回;还可以指定排序条件NSSortDescriptor,即按照某个数据项进行升序或者降序的排列。

    注意:①其中指定参数中的第一个实体是必须的,而后两个NSPredicate和NSSortDescriptor则是可选的,如果不指定NSPredicate,那么返回的是该实体所描述的所有Managered Objects。

    关于如何撰写NSPredicate,这里有详细的介绍:

    https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Predicates/Articles/pSyntax.html

    ②获取数据请求返回的数据是以数组的形式,如果指定了排序条件,那么这些数据就会按某个数据项有序返回。

    通过下面这种图也可以较形象的理解:


     

     

    最后再附上两张图片可以对CoreData有一个整体的认识和弄清之间的关系


     

     

     

     

    下面开始讲一下这个小demo。

    (1)我们通过建立一个master-detail工程(勾选使用CoreData)那么在AppDelegate中就回生成一些CoreData操作中必须的一些方法和属性。如下:

     

    1. @property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;  
    2. @property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;  
    3. @property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;  
    4.   
    5. - (void)saveContext;  
    6. - (NSURL *)applicationDocumentsDirectory;  
    1. @synthesize managedObjectContext = _managedObjectContext;  
    2. @synthesize managedObjectModel = _managedObjectModel;  
    3. @synthesize persistentStoreCoordinator = _persistentStoreCoordinator;  
    4.   
    5. //通过context上下文保存coredata中的数据内容  
    6. //这个方法的通常是在应用程序退出或者推入后台时调用  
    7. - (void)saveContext  
    8. {  
    9.     NSError *error = nil;  
    10.     NSManagedObjectContext *managedObjectContext = self.managedObjectContext;  
    11.     if (managedObjectContext != nil) {  
    12.         if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {  
    13.              // Replace this implementation with code to handle the error appropriately.  
    14.              // 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.   
    15.             NSLog(@"Unresolved error %@, %@", error, [error userInfo]);  
    16.             abort();  
    17.         }   
    18.     }  
    19. }  
    20.   
    21. #pragma mark - Core Data stack  
    22.   
    23. // Returns the managed object context for the application.  
    24. // If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.  
    25. - (NSManagedObjectContext *)managedObjectContext  
    26. {  
    27.     if (_managedObjectContext != nil) {  
    28.         return _managedObjectContext;  
    29.     }  
    30.       
    31.     NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];  
    32.     if (coordinator != nil) {  
    33.         _managedObjectContext = [[NSManagedObjectContext alloc] init];  
    34.         [_managedObjectContext setPersistentStoreCoordinator:coordinator];  
    35.     }  
    36.     return _managedObjectContext;  
    37. }  
    38.   
    39. // Returns the managed object model for the application.  
    40. // If the model doesn't already exist, it is created from the application's model.  
    41. - (NSManagedObjectModel *)managedObjectModel  
    42. {  
    43.     if (_managedObjectModel != nil) {  
    44.         return _managedObjectModel;  
    45.     }  
    46.     NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"CoreDataDemo" withExtension:@"momd"];  
    47.     _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];  
    48.     return _managedObjectModel;  
    49. }  
    50.   
    51. // Returns the persistent store coordinator for the application.  
    52. // If the coordinator doesn't already exist, it is created and the application's store added to it.  
    53. - (NSPersistentStoreCoordinator *)persistentStoreCoordinator  
    54. {  
    55.     if (_persistentStoreCoordinator != nil) {  
    56.         return _persistentStoreCoordinator;  
    57.     }  
    58.       
    59.     NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"CoreDataDemo.sqlite"];  
    60.       
    61.     NSError *error = nil;  
    62.     _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];  
    63.     if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {  
    64.         NSLog(@"Unresolved error %@, %@", error, [error userInfo]);  
    65.         abort();  
    66.     }      
    67.     return _persistentStoreCoordinator;  
    68. }  
    69.   
    70. #pragma mark - Application's Documents directory  
    71.   
    72. // Returns the URL to the application's Documents directory.  
    73. - (NSURL *)applicationDocumentsDirectory  
    74. {  
    75.     return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];  
    76. }  


    以上这部分代码是通用的,可以直接将这部分的内容转到你的Controller中,这样方法的调用都比较简单;当然你也可以在controller中通过delegate获取到这些方法,进行调用,如下所示(注意要#import "AppDelegate.h"):

     

     

    1. AppDelegate *myDelegate = [[UIApplication sharedApplication] delegate];  


    还有一个要说明的是saveContext这个方法的使用,上面的注释中已经有说明使用的情形,例如在AppDelegate中,

     

     

    1. - (void)applicationWillTerminate:(UIApplication *)application  
    2. {  
    3.     // Saves changes in the application's managed object context before the application terminates.  
    4.     [self saveContext];  
    5. }  


    那么假如我们把这些内容转移放到控制器中,那么就可以在添加一个程序推到后台的通知响应,在响应事件的处理中调用这个方法,那么就可以起到保存数据的作用。

     

    说明:最好不要将这些这些coredata操作有关的内容转移到你的controller中,对于小 demo来说是没有问题的,那么如果是对于大一些的工程来说,肯定不是一个controller使用到这些方法,所以还是不做转移好,要使用的地方创建一个AppDelegate实例进行方法调用就好了。

     

    但是下面我讲到的demo中,还是复制了一份到controller中,为的是调用方便。

     

    (2)首先简单介绍这个demo:这个demo会根据一个URL下载一些有关手机的JSON数据,解析后显示在tableview中,而且会保存到coredata中,下一次启动应用程序的时候检测到coredata中有数据就可以不用下载,直接获取数据对tableview进行load数据操作。而且在视图还有一个Fetch按键,用于获取指定的数据内容的操作,然后在终端中显示出来。

     

    该demo中包含两个实体:


     

    下面只显示一些与coredata操作的关键代码,其余部分请下载完整程序代码:下载地址

     

     

    1. //初始化上下文  
    2.     NSManagedObjectContext *context = [self managedObjectContext];  
    3.   
    4.     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{  
    5.           
    6.         //首先查询coredata中是否存在数据,如果存在数据,那么就不用下载数据了,直接用coredata中保存着的数据就可以了  
    7.         NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"DeviceItem" inManagedObjectContext:context];  
    8.         //不设置request的predicate,那么将返回coredata中所有的数据  
    9.         NSFetchRequest *request = [[NSFetchRequest alloc]init];  
    10.         [request setEntity:entityDescription];  
    11.         NSError *error = nil;  
    12.         NSArray *itemsArray = [context executeFetchRequest:request error:&error];  
    13.         //如果获取到的数组元素个数为0,则说明coredata中还没有数据,这时需要下载数据。  
    14.         if ([itemsArray count] == 0) {  
    15.               
    16.             NSLog(@"下载数据中....");  
    17.             NSData* data = [NSData dataWithContentsOfURL: [NSURL URLWithString:@"http://course.gdou.com/Hw/Files/result.json"] ];  
    18.             NSError* error = nil;  
    19.             NSArray *items= [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];  
    20.             if (error != nil)  
    21.                 return;  
    22.             for(NSDictionary *d in items){  
    23.                 // 创建一个新的Managed Object对象并插入到 context 中(尚未保存到数据库中)  
    24.                 //            DeviceItem *di = [NSEntityDescription  
    25.                 //                                               insertNewObjectForEntityForName:@"DeviceItem"  
    26.                 //                                               inManagedObjectContext:context];  
    27.                 // 创建Managed Object 对象的另一种方法:  
    28.                 NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"DeviceItem" inManagedObjectContext:context];  
    29.                 DeviceItem* di = [[DeviceItem alloc] initWithEntity:entityDescription insertIntoManagedObjectContext:context];  
    30.                   
    31.                 di.title=[d valueForKey:@"Title"];  
    32.                 di.iD=[d valueForKey:@"ID"];  
    33.                 di.link=[d valueForKey:@"Link"];  
    34.                 di.imageURL=[d valueForKey:@"ImageURL"];  
    35.                   
    36.                 ImageData *md=[NSEntityDescription  
    37.                                insertNewObjectForEntityForName:@"ImageData"  
    38.                                inManagedObjectContext:context];  
    39.                 md.data=[NSData dataWithContentsOfURL:[NSURL URLWithString:di.imageURL]];  
    40.                 // relations  
    41.                 md.item=di;  
    42.                 di.image=md;  
    43.                 // 保存数据模型对象到数据库中  
    44.                 [context save:&error];  
    45.                   
    46.                 [self.allItems addObject:di];  
    47.                 [self.allImages addObject:md];  
    48.             }  
    49.         }  
    50.         //否则,数组中存在元素,那么直接向tableview中load数据即可  
    51.         else{  
    52.             NSLog(@"从coredata中获取数据中...");  
    53.             for (DeviceItem *item in itemsArray) {  
    54.                 //从coredata中获取到的数据初始化两个数组。  
    55.                 [self.allItems addObject:item];  
    56.                 [self.allImages addObject:item.image];  
    57.             }  
    58.         }  
    59.         dispatch_sync(dispatch_get_main_queue(), ^{  
    60.             block(nil);  
    61.         });  
    62.     });  



     

     

    1. - (IBAction)fetchAction:(id)sender {  
    2.       
    3.     NSManagedObjectContext *context = [self managedObjectContext];  
    4.     NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"DeviceItem" inManagedObjectContext:context];  
    5.     NSFetchRequest *request = [[NSFetchRequest alloc]init];  
    6.     [request setEntity:entityDescription];  
    7.       
    8.     // 取出iD值以23197 起始的设备.   
    9.     // for Predicate Format String Syntax, see: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Predicates/Articles/pSyntax.html  
    10.     NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%K LIKE[c] %@",@"iD",@"23197?"];  
    11.     [request setPredicate:predicate];  
    12.     //按照iD的升序排列  
    13.     NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]  
    14.                                         initWithKey:@"iD" ascending:YES];  
    15.     [request setSortDescriptors:@[sortDescriptor]];  
    16.     NSError *error = nil;  
    17.     NSArray *items = [context executeFetchRequest:request error:&error];  
    18.     //输出iD值以23197 起始的设备的个数。  
    19.     NSLog(@"count = %d",[items count]);  
    20.     //依次输出查询到的数据  
    21.     for (DeviceItem *item in items) {  
    22.         NSString *idString = item.iD;  
    23.         NSString *titleString = item.title;  
    24.         NSLog(@"id : %@,title = %@",idString,titleString);  
    25.     }  
    26.       
    27. }  



     

    大致就是以上的内容了,关于如何coredata的具体xcode操作,可以参考这篇文章 http://blog.csdn.net/q199109106q/article/details/8563438

    我觉得应该讲清楚了,如有错漏的地方,欢迎读者指正。

  • 相关阅读:
    flutter 右滑返回上一页
    flutter 的Animation简单了解
    Flutter Offstage、Visibility隐藏/可见
    flutter手势
    Flutter生命周期
    flutter 路由动画
    flutter 保持页面状态
    flutter 不规则底部工具栏实现
    flutter 主页面底部导航栏实现以及主题风格设置
    flutter DropdownButton使用
  • 原文地址:https://www.cnblogs.com/yulang314/p/3551317.html
Copyright © 2020-2023  润新知