IOS学习——数据持久化(二):初识CoreData

请配合 姊妹篇 食用效果更佳!!!

昨天被问到 SQLite3与Core Data有什么区别,当时只是想到了一个是充分利用了传统的SQL语句,另一个是充分利用了面向对象的编程方法,今天早上想了一会,决定以后再有人问我这个问题我就这么回答:

两个方法都可以完成IOS中数据持久化的任务,都是ORM(object-relational mapping)关系-对象映射的解决方案,SQLIte3本身是一个数据存储的容器,搭配一套方法,单纯滴使用SQLite3类似Java 的Mysql+JDBC,即有一个数据库的实体和一套控制数据库的方法,连接好就可以建立SQL语句,execute就可以了。SQLite3就是这样的,简单粗暴!   另一方面,Core Data是苹果推崇的方法,本身相当于一个库,提供了访问数据类和方法,那么数据存储的另一个要素——存储数据的容器是谁呢?自问自答:可以是二进制文件、xml,而且最重要的其实是SQLIte3,不过这倒是没啥大不了的。

跑偏了!在Core Data提供的存储方法中,不再有数据表——>字段的概念,取而代之的实体(Entity)——>属性(Property)这个对应关系,字段中有value,type,取而代之的是Property中的Attributes(特性)中的attribute name,type (我这种分类应该是和别人的分类方法不一样,因为我想突出这里和数据库的对应关系,其实貌似没啥用呵呵)。而且在存储和访问的时候,通过KVC来从数据库中找到实体和特性,而且就是他们的名字!而且xcode中可视化添加实体和属性,类似phpadmin,但是CoreData能够把实体间的relationship直观表示出来。

(补充一句,上面说分类方式有些奇怪,是因为这个Property下面其实还有三个属性:Attributes,relationship,fetch property,由于后两者是关系和操作方面而非不是数据方面的,所以其实要是没有后两者,Attributes是可以作为Entity的直接下级的,Property存在的概念久淡化了)

所以,如果习惯用SQL语句就用SQLIte3,如果喜欢用面向对象编程就用CoreData,不过有人说有些任务CoreData不能完成,这个我不清楚了,总之对我现在够用了。


回答就说上面那些就够了(认真地),下面在放点东西。

首现想复习一下之前一篇写iOS持久化 的博客,就是这一段话——

1, Managed Object Model
Managed Object Model 是描述应用程序的数据模型,这个模型包含实体(Entity),特性(Property),读取请求(Fetch Request)等。(下文都使用英文术语。)
2,Managed Object Context
Managed Object Context 参与对数据对象(Managed Object)进行各种操作的全过程,并监测数据对象的变化,以提供对 undo/redo 的支持及更新绑定到数据的 UI。
3,Persistent Store Coordinator
Persistent Store Coordinator 相当于数据文件管理器,处理底层的对数据文件的读取与写入。一般我们无需与它打交道。在context的操作下,负责在数据库中生成取出managed object,或者将managed Object存到数据库
4,Managed Object
Managed Object 数据对象,与 Managed Object Context 相关联。

在水果教材上之前的例子中有几处问题没有懂,这个例子是又是贼简单——四个文本框,用户输入啥都行,home时开始存数据到数据库,重新active后读出数据再显示到textField上,说之前我把整段代码都贴上:

//
//  ViewController.m
//  Test Core Data
//
//  Created by 你猜 on 15/11/27.
//  Copyright © 2015年 你猜. All rights reserved.
//

#import "ViewController.h"
#import "AppDelegate.h"

static NSString *const kLineEntityName = @"Line";
static NSString *const kLineNumber = @"lineNumber";
static NSString *const kLineTextKey = @"lineText";

@interface ViewController ()

@property (strong , nonatomic)IBOutletCollection(UITextField) NSArray *lineFields;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    //>>>>>>>>>>>>>>1<<<<<<<<<<<<<<<<
    AppDelegate *appdDelegate = [UIApplication sharedApplication].delegate;
    NSManagedObjectContext *context = [appdDelegate managedObjectContext];
    NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:kLineEntityName];//get table
    
    NSError *error;
    NSArray *object = [context executeFetchRequest:request error:&error];
    if(object==nil){
        NSLog(@"array id empty");
    }
    for(NSManagedObject *oneObject in object){//get attribute in this table >>>>>>>>>>>>>>>>>2<<<<<<<<<<<<<<<<
        int lineNum = [[oneObject valueForKey:kLineNumber] intValue];
        NSString *lineText = [oneObject valueForKey:kLineTextKey];
        
        UITextField *theField = self.lineFields[lineNum];
        theField.text = lineText;
    }
    
    UIApplication *app = [UIApplication sharedApplication];
    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(applicationWillResignActive:) name:UIApplicationWillResignActiveNotification object:app ];
}

-(void)applicationWillResignActive:(NSNotification *)notification {

    AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
    NSManagedObjectContext *context = [appDelegate managedObjectContext];
    NSError *error;
    for(int i = 0 ;i<4 ; i++){
        UITextField *theField = self.lineFields[i];
        
        NSFetchRequest *request = [[NSFetchRequest alloc]initWithEntityName:kLineEntityName];
        NSPredicate *pred = [NSPredicate predicateWithFormat:@"(%K = %d)",kLineNumber,i];
        [request setPredicate:pred];
        
        NSArray *objects = [context executeFetchRequest:request error:&error];
        if(objects==nil){
            NSLog(@"something is wrong!");
        }
        
        NSManagedObject *theLine = nil;
        if([objects count] > 0){
            theLine = [objects objectAtIndex:0];
            
        }else{
//>>>>>>>>>>>>>>>>3<<<<<<<<<<<<<<<<<<
            theLine = [NSEntityDescription insertNewObjectForEntityForName:kLineEntityName inManagedObjectContext:context];                    
<span style="white-space:pre">	</span>}
[theLine setValue:[NSNumber numberWithInt:i] forKey:kLineNumber];
[theLine setValue:theField.text forKey:kLineTextKey];    
   }    
[appDelegate saveContext];
}
- (void)didReceiveMemoryWarning {    [super didReceiveMemoryWarning];    // Dispose of any resources that can be recreated.
}
@end

其实也没有啥,因为在编程中会用到的其实只有ManagedObjectContextManagedObject(哦还有不在这里的NSFetchRequest),下面我就想针对这三个类说一下:

对于小红(知道我说的谁吧)和小绿我想放到一起说,因为她俩是好朋友!看这么一段:

AppDelegate *appdDelegate = [UIApplication sharedApplication].delegate;
NSManagedObjectContext *context = [appdDelegate managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:kLineEntityName];//get table
    
NSError *error;
NSArray *object = [context executeFetchRequest:request error:&error];

**这里再补充点,在之前那篇介绍持久化的博客中,哐哐地把NSManagedObjectContext的定义都写了一遍,其实那个是在工程指定了要用Core Data后在AppDelegate中自动写好啦,前几天比较傻,现在有点豁然开朗的感觉,所以下觉心把这里好好写着。

说上面这段代码,我先把书上的原话码出来 “该方法首先获取对应用委托的引用,我们将使用这个引用获得我们创建的托管对象上下文” -_-#我擦这话组织的无可挑剔,就是完全不懂! 我就理解:有了这两句后,这个context就牛逼了,为啥,因为他承载了APPDelegate中最少三个方法,所谓 “千言万语汇成一个变量”!这之后这个context就可以在本轮数据操作中如入无人之境,因为那三个方法已经把连接数据库,设置App代理完成了,以后就可配合其他参数(如小绿)任意操作数据了。

说完小红,说他的朋友小绿,申请了一个去查询变量,但是,要骑马要指定哪个实体。这里我只想说一个地儿,就是之前看到的声明NSFetchRequest,都跟着一堆,包括指出针对的实体,按照这种init的时候直接指出就好了,当然这里没有加任何谓词,有谓词的话还是要各种set的(比如最大查询条数、大小条件、某时间后等),还是要看本篇的姐妹篇。

哦对了,为啥说小红和小绿是好朋友呢?就是因为这句

NSArray *object = [context executeFetchRequest:request error:&error];

这亲昵程度一目了然。

下面给你,要结合这里说小蓝

NSArray *object = [context executeFetchRequest:request error:&error];
    if(object==nil){
        NSLog(@"array id empty");
    }
    for(NSManagedObject *oneObject in object){//get attribute in this table >>>>>>>>>>>>>>>>>2<<<<<<<<<<<<<<<<

这个object,其实是objects,因为这里存了不止一条查询结果。其实这部分我想说的重点是(这里本来想加一个“自认为是重点“来着,反正没人看索性有删掉了(这心理戏是有多足?))这个oneObject,  注意看他的类型——NSManagedObject!即这个变量存储了这个实体的所有attribute,性命、性别、年龄、所选课程组成的一条结果的所有信息都存在这个oneObject中,嗨就是数据库中的一行,取得时候直接用KVC:

NSString *name = [oneObject valueForKey:@"姓名"] ;

int age = [[oneObject valueForKey:@"年龄"] intValue];

……

<pre name="code" class="objc">        if([objects count] > 0){
            theLine = [objects objectAtIndex:0];
            
        }else{
//>>>>>>>>>>>>>>>>3<<<<<<<<<<<<<<<<<<
            theLine = [NSEntityDescription insertNewObjectForEntityForName:kLineEntityName inManagedObjectContext:context];                    
<span>	</span>}
<span style="white-space:pre">	</span>[theLine setValue:[NSNumber numberWithInt:i] forKey:kLineNumber];
<span style="white-space:pre">	</span>[theLine setValue:theField.text forKey:kLineTextKey];        

 至于这最后一处,就是insert的用法了,不是为了搜索数据。只要得到能控制数据的handler(我想这样称呼),用这个本来就是NSManagedObject实例的theLine变量正合适!按照KVC插入的原则插值就可以了。 

貌似这里想说的就是这些,还有就是关于relationship和fetch property的,有机会聊。。。

最后的最后,还是直接把一直说的姊妹篇中说的那三个函数都贴到这里吧:

新建工程,如果选择了Core Data,会在AppDelegate中写好该写的;把Core Data的头文件链接进来;需要的framework也导入,系统自动写的代码如下:

//
//  AppDelegate.h
//  Test Core Data
//
//  Created by 那强 on 15/11/27.
//  Copyright © 2015年 那强. All rights reserved.
//

#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;

- (void)saveContext;
- (NSURL *)applicationDocumentsDirectory;


@end


//
//  AppDelegate.m
//  Test Core Data
//
//  Created by 那强 on 15/11/27.
//  Copyright © 2015年 那强. All rights reserved.
//

#import "AppDelegate.h"

@interface AppDelegate ()

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    return YES;
}

- (void)applicationWillResignActive:(UIApplication *)application {
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
    // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}

- (void)applicationWillTerminate:(UIApplication *)application {
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
    // Saves changes in the application's managed object context before the application terminates.
    [self saveContext];
}

#pragma mark - Core Data stack

@synthesize managedObjectContext = _managedObjectContext;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;

- (NSURL *)applicationDocumentsDirectory {
    // The directory the application uses to store the Core Data store file. This code uses a directory named "com.tcl.hello.Test_Core_Data" in the application's documents directory.
    return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}

- (NSManagedObjectModel *)managedObjectModel {
    // The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.
    if (_managedObjectModel != nil) {
        return _managedObjectModel;
    }
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Test_Core_Data" withExtension:@"momd"];
    _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    return _managedObjectModel;
}

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
    // The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it.
    if (_persistentStoreCoordinator != nil) {
        return _persistentStoreCoordinator;
    }
    
    // Create the coordinator and store
    
    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"Test_Core_Data.sqlite"];
    NSError *error = nil;
    NSString *failureReason = @"There was an error creating or loading the application's saved data.";
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
        // Report any error we got.
        NSMutableDictionary *dict = [NSMutableDictionary dictionary];
        dict[NSLocalizedDescriptionKey] = @"Failed to initialize the application's saved data";
        dict[NSLocalizedFailureReasonErrorKey] = failureReason;
        dict[NSUnderlyingErrorKey] = error;
        error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict];
        // Replace this 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 _persistentStoreCoordinator;
}


- (NSManagedObjectContext *)managedObjectContext {
    // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.)
    if (_managedObjectContext != nil) {
        return _managedObjectContext;
    }
    
    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (!coordinator) {
        return nil;
    }
    _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    [_managedObjectContext setPersistentStoreCoordinator:coordinator];
    return _managedObjectContext;
}

#pragma mark - Core Data Saving support

- (void)saveContext {
    NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
    if (managedObjectContext != nil) {
        NSError *error = nil;
        if ([managedObjectContext hasChanges] && ![managedObjectContext 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();
        }
    }
}

@end

就酱。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值