IOS資料儲存之CoreData使用優缺點

總李寫程式碼發表於2016-05-13

前言:

   學習了Sqlite資料之後認真思考了一下,對於已經習慣使用orm資料庫的開發者或者對sql語句小白的開發者來說該如何做好資料庫開發呢?這個上網搜了一下?看來總李多慮了!apple 提供了一種資料方式,它就是今天的主角:CoreData!我們一起來探究它是否能夠滿足我們專案開發的需要呢?

CoreData介紹:

   Core Date是ios3.0後引入的資料持久化解決方案,它是是蘋果官方推薦使用的,不需要藉助第三方框架。Core Date實際上是對SQLite的封裝,提供了更高階的持久化方式。在對資料庫操作時,不需要使用sql語句,也就意味著即使不懂sql語句,也可以運算元據庫中的資料。

CoreData優點:

    Core Data實際上是將資料庫的建立、表的建立、物件和表的轉換等操作封裝起來,極大的簡化了我們的操作。Core Date與SQLite相比較,SQLite比較原始,操作比較複雜,使用的是C的函式對資料庫進行操作,但是SQLite可控性更強,並且能夠跨平臺。

CoreData缺點:

      儲存效能一般,預設建立的表沒有主鍵,效率低,複雜的查詢更是不敢想像。CoreData 對批量資料的處理執行的不太好,查資料好像說IOS8推出了批量處理的一些方式。對於多執行緒的支援也不太好,我是xcode 7上開發的每一個entity都要生成4個類,打個比方一個專案中要建10個表那就要維護40個類,你說累不累?

怎麼使用CoreData?

   第一步:在專案中引入CoreData.framework

    第二步:建立xxxx.xcdatamodeld

     第三步:模型物件的實體

 

接下來就是具體實現:具體實現之前先來認識如下幾個物件

(1)NSManagedObjectModel(被管理的物件模型)

           相當於實體,不過它包含 了實體間的關係

  (2)NSManagedObjectContext(被管理的物件上下文)

         操作實際內容

        作用:插入資料  查詢  更新  刪除

 (3)NSPersistentStoreCoordinator(持久化儲存助理)

          相當於資料庫的聯結器

   (4)NSFetchRequest(獲取資料的請求)    

        相當於查詢語句

   (5)NSPredicate(相當於查詢條件)

   (6)NSEntityDescription(實體結構)

為了方便實現,本文整理一個資料管理類來測試CoreData:CoreDataManager

CoreDataManager.h

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

@interface CoreDataManager : NSObject<NSCopying>

@property(strong,nonatomic,readonly)NSManagedObjectModel* managedObjectModel;//管理資料模型

@property(strong,nonatomic,readonly)NSManagedObjectContext* managedObjectContext;//管理資料內容

@property(strong,nonatomic,readonly)NSPersistentStoreCoordinator* persistentStoreCoordinator;//持久化資料助理

//建立資料庫管理者單例
+(instancetype)shareManager;

//插入資料
-(void)insertData:(NSString*)tempName;

//刪除資料
-(void)deleteData;

//刪除資料
-(void)deleteData:(NSString*)tempName;

//查詢資料
-(void)queryData;

//根據條件查詢
-(void)queryData:(NSString*)tempName;

//更新資料
-(void)updateData:(NSString*)tempName;

@end
View Code

CoreDataManager.m

#import "CoreDataManager.h"
#import "Car.h"

static CoreDataManager *shareManager=nil;

@implementation CoreDataManager

@synthesize managedObjectContext =_managedObjectContext;

@synthesize managedObjectModel = _managedObjectModel;

@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;

//例項化物件
-(instancetype)init
{
    self=[super init];
    if (self) {
        
    }
    return self;
}

//建立資料庫管理者單例
+(instancetype)shareManager
{
    //這裡用到了雙重鎖定檢查
    if(shareManager==nil){
        @synchronized(self){
            if(shareManager==nil){
                shareManager =[[[self class]alloc]init];
            }
        }
    }
    return shareManager;
}

-(id)copyWithZone:(NSZone *)zone
{
    
    return shareManager;
}

+(id)allocWithZone:(struct _NSZone *)zone
{
    if(shareManager==nil){
        shareManager =[super allocWithZone:zone];
    }
    return shareManager;
}

//託管物件
-(NSManagedObjectModel *)managedObjectModel
{
    if (_managedObjectModel!=nil) {
        return _managedObjectModel;
    }
    
    NSURL* modelURL=[[NSBundle mainBundle] URLForResource:@"myCoreData" withExtension:@"momd"];
    _managedObjectModel=[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    return _managedObjectModel;
}

//託管物件上下文
-(NSManagedObjectContext *)managedObjectContext
{
    if (_managedObjectContext!=nil) {
        return _managedObjectContext;
    }
    
    NSPersistentStoreCoordinator* coordinator=[self persistentStoreCoordinator];
    if (coordinator!=nil) {
        _managedObjectContext=[[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];//NSMainQueueConcurrencyType NSPrivateQueueConcurrencyType
        
        [_managedObjectContext setPersistentStoreCoordinator:coordinator];
    }
    return _managedObjectContext;
}

//持久化儲存協調器
-(NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
    if (_persistentStoreCoordinator!=nil) {
        return _persistentStoreCoordinator;
    }
    NSString* docs=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject];
    NSURL* storeURL=[NSURL fileURLWithPath:[docs stringByAppendingPathComponent:@"myCoreData.sqlite"]];
    NSLog(@"path is %@",storeURL);
    NSError* error=nil;
    _persistentStoreCoordinator=[[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
        NSLog(@"Error: %@,%@",error,[error userInfo]);
    }
    return _persistentStoreCoordinator;
}

//插入資料
-(void)insertData:(NSString*)tempName
{
    //讀取類
    Car *car=[NSEntityDescription insertNewObjectForEntityForName:@"Car" inManagedObjectContext:self.managedObjectContext];
    car.name=tempName;
    //儲存
    NSError *error;
    [self.managedObjectContext save:&error];
}

//刪除資料
-(void)deleteData
{
    //建立讀取類
    NSEntityDescription *entity =[NSEntityDescription entityForName:@"Car" inManagedObjectContext:self.managedObjectContext];
    
    //建立連線
    NSFetchRequest* request=[[NSFetchRequest alloc] init];
    [request setEntity:entity];
    
    //啟動查詢
    NSError *error;
    NSArray *deleteArr=[self.managedObjectContext executeFetchRequest:request error:&error];
    if(deleteArr.count){
        for (Car *car in deleteArr) {
            [self.managedObjectContext deleteObject:car];
        }
        NSError *error;
        [self.managedObjectContext save:&error];
    }else{
        NSLog(@"未查詢到可以刪除的資料");
    }

}

//刪除資料
-(void)deleteData:(NSString*)tempName;
{
    //建立讀取類
    NSEntityDescription *entity =[NSEntityDescription entityForName:@"Car" inManagedObjectContext:self.managedObjectContext];
    
    //建立連線
    NSFetchRequest* request=[[NSFetchRequest alloc] init];
    [request setEntity:entity];
    
    //建立檢索條件
    NSPredicate *predicate =[NSPredicate predicateWithFormat:@"name=%@",tempName];
    [request setPredicate:predicate];
    
    //啟動查詢
    NSError *error;
    NSArray *deleteArr=[self.managedObjectContext executeFetchRequest:request error:&error];
    if(deleteArr.count){
        for (Car *car in deleteArr) {
            [self.managedObjectContext deleteObject:car];
        }
         NSError *error;
        [self.managedObjectContext save:&error];
    }else{
        NSLog(@"未查詢到可以刪除的資料");
    }
}


//查詢資料
-(void)queryData
{
    //建立讀取類
    NSEntityDescription *entity =[NSEntityDescription entityForName:@"Car" inManagedObjectContext:self.managedObjectContext];
    
    //建立連線
    NSFetchRequest* request=[[NSFetchRequest alloc] init];
    [request setEntity:entity];
    
    //啟動查詢
    NSError *error;
    NSArray *carArr=[self.managedObjectContext executeFetchRequest:request error:&error];
    for(Car *car in carArr){
        NSLog(@"car---->%@",car.name);
    }
    
}

-(void)queryData:(NSString*)tempName
{
    //建立讀取類
    NSEntityDescription *entity =[NSEntityDescription entityForName:@"Car" inManagedObjectContext:self.managedObjectContext];
    
    //建立連線
    NSFetchRequest* request=[[NSFetchRequest alloc] init];
    [request setEntity:entity];
    
    //建立檢索條件
    NSPredicate *predicate =[NSPredicate predicateWithFormat:@"name=%@",tempName];
    [request setPredicate:predicate];
    
    //啟動查詢
    NSError *error;
    NSArray *carArr=[self.managedObjectContext executeFetchRequest:request error:&error];
    for(Car *car in carArr){
        NSLog(@"car---->%@",car.name);
    }
    
}

//更新資料
-(void)updateData:(NSString*)tempName
{
    //建立讀取類
    NSEntityDescription *entity =[NSEntityDescription entityForName:@"Car" inManagedObjectContext:self.managedObjectContext];
    
    //建立連線
    NSFetchRequest* request=[[NSFetchRequest alloc] init];
    [request setEntity:entity];
    
    //建立檢索條件
    NSPredicate *predicate =[NSPredicate predicateWithFormat:@"name=%@",tempName];
    [request setPredicate:predicate];
    
    //啟動查詢
    NSError *error;
    NSArray *deleteArr=[self.managedObjectContext executeFetchRequest:request error:&error];
    if(deleteArr.count){
        for (Car *car in deleteArr) {
            car.name=@"test";
        }
        NSError *error;
        [self.managedObjectContext save:&error];
    }else{
        NSLog(@"未查詢到可以刪除的資料");
    }

}

@end
View Code

 具體使用方式

    //清空資料
           [[CoreDataManager shareManager]deleteData];
           //插入10條資料
           for(int i=0;i<10;i++){
               NSString *string = [[NSString alloc] initWithFormat:@"%d",i];
               [[CoreDataManager shareManager]insertData:string];
           }
           //然後查詢一下
            [[CoreDataManager shareManager]queryData];
           //然後刪除一條資料
            [[CoreDataManager shareManager]deleteData:@"5"];
           //然後查詢一下
           [[CoreDataManager shareManager]queryData];
           // 更新資料
           [[CoreDataManager shareManager]updateData:@"3"];
           //然後查詢一下
           [[CoreDataManager shareManager]queryData];
           
           //清空資料
           [[CoreDataManager shareManager]deleteData];
           
           //然後查詢一下
           [[CoreDataManager shareManager]queryData];
           

測試一下效率:測試資料10000條

 NSMutableArray *testArray =[[NSMutableArray alloc]init];
           int testMaxCount =10000;
           for(int i=0;i<testMaxCount;i++){
               NSString *string = [[NSString alloc] initWithFormat:@"%d",i];
              [testArray addObject:string];
           }
           
            //測試一下效率  第1種
           CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
           for(NSString *tempName in testArray){
                [[CoreDataManager shareManager]insertData:tempName];
           }
           CFAbsoluteTime end=CFAbsoluteTimeGetCurrent();
           NSLog(@"coreData資料插入 time cost: %0.3f", end - start);
           
           //測試一下效率  第2種
             start = CFAbsoluteTimeGetCurrent();
            [[CoreDataManager shareManager]insertDatas:testArray];
             end=CFAbsoluteTimeGetCurrent();
            NSLog(@"coreData資料插入 time cost: %0.3f", end - start);
insertData函式:
//插入資料
-(void)insertData:(NSString*)tempName
{
    //讀取類
    Car *car=[NSEntityDescription insertNewObjectForEntityForName:@"Car" inManagedObjectContext:self.managedObjectContext];
    car.name=tempName;
    //儲存
    NSError *error;
    [self.managedObjectContext save:&error];
}
insertDatas函式:
//插入資料
-(void)insertDatas:(NSArray*)tempNames
{
    for(NSString *name in tempNames){
        Car *car=[NSEntityDescription insertNewObjectForEntityForName:@"Car" inManagedObjectContext:self.managedObjectContext];
        car.name=name;
    }
    NSError *error;
    [self.managedObjectContext save:&error];
    
}

執行結果:

第一種:8.408

第二種:0.162

但是有個超級大的問題,第二種方式雖然效率高,但是插入資料亂序。第一種正常但是效率超低,同樣近似的資料量sqlite效率比這個高不知多少倍。如果用這個來做資料庫開發我覺得是無愛了。批量操作支援的不太好。對於它的資料庫升級也不想過多瞭解。所以就隨便查了下,得出如下結論:CoreData 的資料模型升級相容性比較差,如果模型不對,會導致程式連起都起不來。雖然提供了模型升級程式碼,但是在客戶端的管理模型版本管理也會相對複雜。

 

相關文章