天天看點

iOS開發之Core Data資料存儲

Core Data是iOS5之後才出現的一個架構,它提供了對象-關系映射(ORM)的功能即能夠将OC對象轉化成資料,儲存在SQLite資料庫檔案中,也能夠将儲存在資料庫中的資料還原成OC對象。

傳統的資料庫要把資料寫到資料庫,而且要寫SQL語句 Core Data 就避免了寫SQL語句的麻煩了

       CoreData底層的持久化存儲方式可以是SQLite資料庫,也可以是XML檔案,甚至可以直接以記憶體作為持久化儲存設備(如果選用該存儲方式,那麼應用重新開機時資料會丢失)。

       CoreData的核心概念是實體。實體是由Core Data管理的模型對象,它必須是NSManagedObject類或其子類的執行個體。實體與實體之間存在1-1、1-N、N-N的關聯關系。整個應用的所有實體以及實體之間的關聯關系被稱為托管對象模型。

       CoreData的核心對象是托管對象上下文(NSManagedObjectContext,有時簡稱上下文),所有實體都處于托管對象上下文管理中,Core Data應用對實體所做的任何增、删、改、查操作都必須通過托管對象上下文來完成。

       NSManagedObjectContext底層又與持久化存儲協調器(NSPersistentStoreCoordinator)銜接。持久化存儲協調器負責管理底層的存儲形式,比如利用SQLite存儲。

下面來總結Core Data中的核心API:

1.    實體(NSManagedObject):代表實體。實體必須是該類或該類的子類

2.    托管對象模型(NSManagedObjectModel):該對象負責管理整個應用的所有實體以及實體之間的關聯關系。

3.    持久化存儲協調器(NSpersistentStoreCoordinator):負責管理底層的存儲方式,如利用SQLite存儲。

4.    托管對象上下文(NSManagedObjectContext):對實體做的增、删、改、查操作都必須通過托管對象上下文來完成。

5.    實體描述(NSEntityDescription):該對象代表了關于某個實體的描述資訊。從某種角度來說,該對象相當于實體的抽象。

6.    擷取請求(NSfetchRequest):該對象封裝了查詢實體的請求,包括程式需要查詢哪些實體、查詢條件、排序規則等。

下面介紹一下使用Core Data的步驟:

1.    建立NSManageObjectModel對象來加載管理應用的托管對象模型。

2.    以NSManageObjectModel對象為基礎,根據實際建立NSPersistentStoreCoordinator對象,該對象确定Core Data的底層存儲形式。

3.    以NSManageObjectModel對象為基礎,建立NSmanagedObjectContext對象,該對象是Core Data進行持久化通路的核心對象。

4.    利用擷取執行個體(NSFectRequest)再調用NSmanagedObjectContext的executeFetchRequest:error:方法執行查詢,找到符合條件的實體,再進行執行增、删、改、查操作,然後調用NSmanagedObjectContext對象的save:方法将修改白村到底層儲存設備。

下面我們還是來通過存儲學生資訊為例,來講解CoreData的簡單使用。

建立項目,勾選Use Core Data複選框,我這裡的項目名為:01-CoreData

iOS開發之Core Data資料存儲

create之後,會在項目導航區看到下圖檔案

iOS開發之Core Data資料存儲

還會在appDelegate類中發現多了幾個屬性和方法,如下:

appDelegate.h

#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;
/**
 *  擷取Document的URL路徑
 */
- (NSURL *)applicationDocumentsDirectory;
@end
           

appDelegate.m

#pragma mark - Core Data stack

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


/**
 *  擷取Document的URL路徑
 */
- (NSURL *)applicationDocumentsDirectory {
    return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}

/**
 *  初始化托管對象模型(實體以及實體之間的關聯關系)
 */
- (NSManagedObjectModel *)managedObjectModel {
    if (_managedObjectModel != nil) {
        return _managedObjectModel;
    }
    // 擷取實體模型檔案對應的URL
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"_1_CoreData" withExtension:@"momd"];
    // 根據URL建立并初始化托管模型對象
    _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    return _managedObjectModel;
}

/**
 *  初始化持久化存儲協調器(Core Data底層的存儲形式)
 */
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
    if (_persistentStoreCoordinator != nil) {
        return _persistentStoreCoordinator;
    }
    
    // 以托管模型對象建立并初始化協調器
    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    
    // 擷取SQlLite資料庫的存儲目錄
    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"_1_CoreData.sqlite"];
    
    NSError *error = nil;
    NSString *failureReason = @"There was an error creating or loading the application's saved data.";
    // 設定底層資料存儲機制為SQLite資料庫,如果設定失敗則傳回錯誤資訊
    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];
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }
    
    return _persistentStoreCoordinator;
}

/**
 *  初始化托管對象上下文
 */
- (NSManagedObjectContext *)managedObjectContext {
    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]) {
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
    }
}
           

并且在應用的代理方法中有:(當應用退出後,會儲存上下文的修改)

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

點選.xcodemodeld檔案,在工作區會出現如圖所示界面

iOS開發之Core Data資料存儲

點選左下角add Entity按鈕添加實體

iOS開發之Core Data資料存儲

然後在ENTITIES區會出現如圖所示實體,再重命名實體名,我這裡重命名為:StudentEntity

iOS開發之Core Data資料存儲

在該實體的右側會出現添加屬性界面,點選添加屬性

iOS開發之Core Data資料存儲

屬性添加完之後,點選xcode導航欄上的Editor -> Create NSManageObject Subclass自動建立實體類,如下:

iOS開發之Core Data資料存儲

這些準備工作做好以後,我們就可以向資料庫_1_CoreData.sqlite中進行資料的增、删、該、查操作了。

添加學生資訊:

iOS開發之Core Data資料存儲

點選添加按鈕,将學生資訊插入資料庫中(這裡不考慮重複插入)

/**
 *  添加學生資訊
 */
- (IBAction)addClick {
    
    // 利用NSEntityDescription擷取StudentEntity實體
    StudentEntity *stu = [NSEntityDescription insertNewObjectForEntityForName:@"StudentEntity" inManagedObjectContext:[AppDelegate shareAppDelegate].managedObjectContext];
    stu.name = self.nameTextField.text;
    stu.age = [NSNumber numberWithInteger:[self.ageTextField.text integerValue]];
    stu.sex = [NSNumber numberWithInt:[self.sexTextField.text intValue]];
    
    NSError *error = nil;
    [[AppDelegate shareAppDelegate].managedObjectContext save:&error];
    if (error) {
        NSLog(@"添加失敗");
    }else {
        NSLog(@"添加成功");
    }
}
           

點選添加按鈕後,會在應用沙盒中發現下圖檔案:

iOS開發之Core Data資料存儲

列印結果:

iOS開發之Core Data資料存儲

現在我們向資料庫中多插入幾條資料,然後來做查詢操作,看看資料庫中有沒有資料。查詢操作如下:

/**
 *  查詢所有學生資訊,并列印
 */
- (IBAction)queryClick {
    // 得到擷取資料的請求對象
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"StudentEntity"];
    // 排序規則(查詢結果按年齡降序排序)
    NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:NO];
    request.sortDescriptors = @[sort];
    
    // 執行查詢
    NSError *error = nil;// 錯誤資訊
    NSArray *result = [[AppDelegate shareAppDelegate].managedObjectContext executeFetchRequest:request error:&error];
    if (error) {
        NSLog(@"查詢操作失敗");
    }else {
        if (result.count <= 0) {
            NSLog(@"無學習資訊");
        }else {
            NSLog(@"列印學生資訊:");
            for (StudentEntity *stu in result) {
                NSLog(@"姓名:%@,年齡:%lu,性别:%d", stu.name, stu.age.integerValue, stu.sex.intValue);
            }
        }
    }
}
           

列印出來看看,我們剛剛添加的多條學生資訊

iOS開發之Core Data資料存儲

結果就是我們添加的學生,證明我們的資料已經添加到資料庫中了。

我們先來删除指定姓名(如:shx1)的學生資訊:(資料庫中所有叫shx1的學生會全部删除)

iOS開發之Core Data資料存儲
/**
 *  删除指定姓名的學生資訊
 */
- (IBAction)deleteClick {
    // 先周遊資料庫,查詢有無該學生資訊,無該學生則不用執行删除操作
    
    // 得到擷取資料的請求對象
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"StudentEntity"];
    // 查詢條件
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name = %@", self.deleteNameTextField.text];
    request.predicate = predicate;
    // 排序規則(按年齡升序)
    NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:NO];
    request.sortDescriptors = @[sort];
    // 執行查詢
    NSError *error = nil;// 錯誤資訊
    NSArray *result = [[AppDelegate shareAppDelegate].managedObjectContext executeFetchRequest:request error:&error];
    // 查詢結果
    if (error) {
        NSLog(@"查詢操作失敗");
    }else {
        if (result.count) {// 有該學生資訊,可執行删除操作
            for (StudentEntity *stu in result) {
                // 删除學生資訊
                [[AppDelegate shareAppDelegate].managedObjectContext deleteObject:stu];
                NSLog(@"删除成功");
                
            }
            // 删除資料後,儲存
            NSError *saveError = nil;
            [[AppDelegate shareAppDelegate].managedObjectContext save:&saveError];
            if (saveError) {
                NSLog(@"删除,儲存失敗");
            }else {
                NSLog(@"删除,儲存成功");
            }
        }else {
            NSLog(@"無該學生資訊");
        }
    }
}
           

輸入要删除的學生姓名:shx1列印結果:

iOS開發之Core Data資料存儲

我們再查詢所有學生資訊,并列印,看看shx1還在不在,點選查詢:

iOS開發之Core Data資料存儲

姓名叫shx1的學生沒有了,證明我們已經删除了。

現在我們來修改某個學生(比如:shx5)的年齡。(原shx5的年齡從上面指定為22,現在我們将他的年齡改為100,資料庫中所有叫shx5的學生的年齡都會被改成100)。

iOS開發之Core Data資料存儲
/**
 *  修改某個學生的年齡
 */
- (IBAction)tureClick {
    // 先周遊資料庫,查詢有無該學生資訊,無該學生則不能修改年齡
    
    // 得到擷取資料的請求對象
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"StudentEntity"];
    // 查詢條件
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name = %@", self.updateNameTextField.text];
    request.predicate = predicate;
    // 執行查詢
    NSError *error = nil;// 錯誤資訊
    NSArray *result = [[AppDelegate shareAppDelegate].managedObjectContext executeFetchRequest:request error:&error];
    // 查詢結果
    if (error) {
        NSLog(@"查詢操作失敗");
    }else {
        if (result.count) {// 有該學生資訊,可執行修改操作
            for (StudentEntity *stu in result) {
                // 修改學生資訊
                stu.age = [NSNumber numberWithInt:self.updateAgeTextField.text.intValue];
                NSLog(@"修改成功");
                
            }
            // 删除資料後,儲存
            NSError *saveError = nil;
            [[AppDelegate shareAppDelegate].managedObjectContext save:&saveError];
            if (saveError) {
                NSLog(@"修改,儲存失敗");
            }else {
                NSLog(@"修改,儲存成功");
            }
        }else {
            NSLog(@"無該學生資訊");
        }
    }
}
           

姓名輸入shx5,年齡輸入100,列印結果:

iOS開發之Core Data資料存儲

我們查詢所有的學生資訊,并列印出來,看看shx5的年齡是不是100。

iOS開發之Core Data資料存儲

姓名叫shx5的學生的年齡已經改為100了,證明我們的資料庫操作是正确的。

以上是關于Core Data的簡單使用,更進一步的使用我也在學習中。