你好,歡迎來到IOS教程網

 Ios教程網 >> IOS訊息 >> 關於IOS >> Core Data使用(中篇)

Core Data使用(中篇)

編輯:關於IOS

上一篇文章我們從一個小例子講解了Core Data的使用方式,其實盡管概念很多,但實際應用的時候大多數情況下我們不需要接觸太多的類和方法,只要拿到context並且操作即可。

這次我們看看iOS自帶的一個小例子,原文參見:Core Data Tutorial for iOS。由於上一次我們已經講述了基本概念和實現方法,這次我的描述會簡練一些,不再一步一步詳細講解。

本次目標是創建一個應用程序,可以記錄每次你保存的經緯度坐標,並且可以對這些坐標(我們保存為一個Event實體)進行編輯。

我的演示環境是XCode 4.6.3,所以有些地方和原文是不同的,這裡都已我自己的環境為准。

建立工程

步驟

  1. 創建一個Empty Application,起名叫Locations,選擇Devices為iPhone,並且使用ARC;
  2. 添加CoreLocation.framework;
  3. 添加一個Storyboard文件,並在工程屬性中選擇Main Storyboard為這個文件;
  4. 至此,操作步驟完成。

對工程的理解

以上步驟完成後,我們的工程中多了很多文件,我們一一講解如下:

  1. 應用的代理類AppDelegate(.h和.m文件);
  2. 一個.xib界面描述文件;
  3. 一個Core Data的模型文件.xcdatamodelId;
  4. 這個工程還引用了CoreLocation.framework;

我們還能看到對於AppDelegate.h,會自動生成相關Core Data的處理:

[cpp] view plaincopy

  1. @property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
  2. @property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
  3. @property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
  4. - (void)saveContext;
  5. - (NSURL *)applicationDocumentsDirectory;

就像方法暗指的那樣,applicationDocumentsDirectory方法返回的是app的文檔路徑,數據將被保存在那裡。

Core Data Stack

這裡的stack指的是Core Data framework中一系列對象的集合,這些對象協同工作,將模型從文件中取出,或者寫入到文件中。從類比的角度說,Core Data可以被看為一個數據庫,保存了記錄和表(Core Data中的數據有可能真的被保存在數據庫中,例如SQLite,但是這個不是一定的)。一個最簡單和最頻繁使用的Core Data Stack結構如下(可以和上一篇文章印證):

其中你最常用的是Managed Object Context和其包含的Managed Object對象。

Managed Object和Managed Object Context

Managed Object是一個NSManagedObject或者他的子類的對象。他代表了在Store中保存的一個對象的記錄,所以他是一個由Core Data管理的模型。Managed Object對象管理應用中使用的數據,例如:一個人力資源管理系統中的部門和雇員信息;一個繪圖程序中的形狀、文本和分組信息;一個音樂程序中的音軌信息。一個Managed Object始終和一個Managed Object Context相關聯。

Managed Object Context是一個NSManagedObjectContext類的實例。一個context代表了一個應用中與數據有關的內存區域。context的主要職責是用來管理managed object對象。Context對象是你的應用的控制中樞,負責生命周期管理到驗證、Managed Object間的關系維護、重做和取消等功能。

當你新建一個Managed Object的時候,首先要將其放入Context中。你將對象取回(fetch)的時候,同樣將其存入context對象。所有的操作都在內存中,一直到你通過commit提交你的修改為止。

下面的圖演示了記錄(record)和對象(object)的關系,注意有個Nigel對象沒有保存,所以Store中的salary仍然是50000,另外有兩個對象未納入context管理。

Managed Object Model

Managed Object Model是一個NSManagedObjectModel類的實例,他描述了數據庫的Schema,所以你的數據能夠保存到Store中。一個模型(model)是一個實體對象模型(NSEntityDescription)的集合。一個實體描述根據實體的名字描述一個實體,類名,及其屬性。

下圖描述了模型的實體描述、數據庫中表和Managed Object中單個對象的關系:

每個Managed Object都有對應的實體描述。

Core Data使用模型來在應用的managed object和數據庫的記錄中描述映射關系。所以一定要對改變模型(描述)小心處理。Core Data無法使用你之前的模型來使用現在的數據。(實際上需要通過版本管理來處理這種變更,而不是完全不能處理)

Persistent Store Coordinator

PSC扮演的角色是告訴Core Data如何管理數據。大多數時候你完全不用關心這個對象。

一個PSC是NSPersistentStoreCoordinator類的一個實例,他管理了一系列的persistent object store。一個persistent object stores代表一個需要序列化的外部文件或數據庫。實際上Core Data支持多種持久化數據類型,甚至你可以聲明自己的數據文件類型。

在iOS中,一般你僅有一個store,但在復雜的應用(例如OSX)中你可能有多個保存實體的store,所以PSC需要來管理這些store,當你需要取回數據時,PSC將取回所有結果。

下圖模擬了coordinator的工作角色,實際情況通常不會這麼復雜:

Table View Controller

這一小節主要實現Table View Controller相關功能。下面我們一步一步來執行以下步驟,:

  1. 在Storyboard中創建一個Table View,並將其嵌入到Navigation Controller中;
  2. 添加兩個按鈕到Navigation Bar上面,並且將左邊按鈕的Identifier改為Edit,右邊Identifier修改為Add;
  3. 添加一個UITableViewController的子類,名字為LocationsListViewController;
  4. 添加對位置信息的支持,所以LocationsListViewController的頭文件變為: [cpp] view plaincopy

    1. #import <UIKit/UIKit.h>
    2. #import <CoreLocation/CoreLocation.h>
    3. @interface LocationsListViewController : UITableViewController <CLLocationManagerDelegate>
    4. @property (nonatomic, retain) NSMutableArray *eventsArray;
    5. @property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
    6. @property (nonatomic, retain) CLLocationManager *locationManager;
    7. @property (nonatomic, retain) UIBarButtonItem *addButton;
    8. @end
  5. 實現文件中對於location獲取相關代碼實現如下: [cpp] view plaincopy

    1. @implementation LocationsListViewController
    2. @synthesize eventsArray;
    3. @synthesize locationManager;
    4. @synthesize managedObjectContext;
    5. @synthesize addButton;
    6. - (CLLocationManager *)locationManager {
    7.     if (locationManager != nil) {
    8.         return locationManager;
    9.     }
    10.     locationManager = [[CLLocationManager alloc] init];
    11.     locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
    12.     locationManager.delegate = self;
    13.     return locationManager;
    14. }
    15. - (void)locationManager:(CLLocationManager *)manager
    16.     didUpdateToLocation:(CLLocation *)newLocation
    17.            fromLocation:(CLLocation *)oldLocation {
    18.     addButton.enabled = YES;
    19. }
    20. - (void)locationManager:(CLLocationManager *)manager
    21.        didFailWithError:(NSError *)error {
    22.     addButton.enabled = NO;
    23. }
    24. - (id)initWithStyle:(UITableViewStyle)style
    25. {
    26.     self = [super initWithStyle:style];
    27.     if (self) {
    28.         // Custom initialization
    29.     }
    30.     return self;
    31. }
    32. - (void)viewDidLoad
    33. {
    34.     [super viewDidLoad];
    35.     // Set the title.
    36.     self.title = @"Locations";
    37.     // Set up the buttons.
    38.     self.navigationItem.leftBarButtonItem = self.editButtonItem;
    39.     addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd
    40.                                                               target:self action:@selector(addEvent)];
    41.     addButton.enabled = NO;
    42.     self.navigationItem.rightBarButtonItem = addButton;
    43.     // Start the location manager.
    44.     [[self locationManager] startUpdatingLocation];
    45. }

    這裡剩下addEvent未實現。

  6. 接著我們去AppDelegate類,將didFinishLaunchingWithOptions修改為: [cpp] view plaincopy

    1. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    2. {
    3.     return YES;
    4. }
  7. 至此,本階段工作完成,運行程序顯示如下界面:

Managed Object and Model

這一部分比較簡單,步驟如下:

  1. 打開Locations.xcdatamodeld文件,Add Entity,名字叫Event;
  2. 分別添加屬性如下圖:
  3. 創建自定義的Managed Object類。原則上你可以直接使用NSManagedObject,但實際上大部分情況下都會使用他的子類,好處如下:
    • 從開發工具獲得更好的支持。例如屬性存取方法的自動完成、編譯時類型和符號的檢測等;
    • 支持實體的自定義方法。大多數情況下你希望實體提供特殊的邏輯,例如自定義的驗證、或者衍生的屬性等。例如,一個Person實體的hasDriversLicense屬性在age屬性不到17時不可能是true(所以會有一個校驗),或者一個fullName方法返回的是firstName和lastName的一個合適的拼接。
  4. 我們選擇Event這個Entity,然後新建一個文件,選擇Core Data分類中的NSManagedObject subclass。
  5. 我們可以看到自動生成的代碼中包含了Event這個實體映射的域模型了。

Adding Events

這一小節的目標是添加事件並且將其顯示在ListView中。

  1. 在story board中將我們的list view和我們之前創建的LocationsListViewController相關聯;
  2. 在LocationsListViewController中添加方法: [cpp] view plaincopy

    1. - (void)addEvent {
    2.     // Get the Current Location
    3.     CLLocation *location = [locationManager location];
    4.     if (!location) {
    5.         return;
    6.     }
    7.     // Create and configure a new instance of the Event entity.
    8.     Event *event = (Event *)[NSEntityDescription insertNewObjectForEntityForName:@"Event" inManagedObjectContext:managedObjectContext];
    9.     CLLocationCoordinate2D coordinate = [location coordinate];
    10.     [event setLatitude:[NSNumber numberWithDouble:coordinate.latitude]];
    11.     [event setLongitude:[NSNumber numberWithDouble:coordinate.longitude]];
    12.     [event setCreationDate:[NSDate date]];
    13.     // Save the New Event
    14.     NSError *error = nil;
    15.     if (![managedObjectContext save:&error]) {
    16.         // Handle the error.
    17.         NSLog(@"Save Event failed.");
    18.         return ;
    19.     }
    20.     // Update the Events Array and the Table View
    21.     [eventsArray insertObject:event atIndex:0];
    22.     NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
    23.     [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
    24.                           withRowAnimation:UITableViewRowAnimationFade];
    25.     [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
    26. }
  3. 其余涉及到Table View的顯示代碼如下(注意設置Table View的Cell的Identifier為Cell,區分大小寫;同時將Cell的Style設置為Right Detail): [cpp] view plaincopy

    1. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
    2. {
    3.     // Return the number of sections.
    4.     return 1;
    5. }
    6. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    7. {
    8.     return [eventsArray count];
    9. }
    10. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    11. {
    12.     // A date formatter for the time stamp.
    13.     static NSDateFormatter *dateFormatter = nil;
    14.     if (dateFormatter == nil) {
    15.         dateFormatter = [[NSDateFormatter alloc] init];
    16.         [dateFormatter setTimeStyle:NSDateFormatterMediumStyle];
    17.         [dateFormatter setDateStyle:NSDateFormatterMediumStyle];
    18.     }
    19.     // A number formatter for the latitude and longitude.
    20.     static NSNumberFormatter *numberFormatter = nil;
    21.     if (numberFormatter == nil) {
    22.         numberFormatter = [[NSNumberFormatter alloc] init];
    23.         [numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
    24.         [numberFormatter setMaximumFractionDigits:3];
    25.     }
    26.     static NSString *CellIdentifier = @"Cell";
    27.     // Dequeue or create a new cell.
    28.     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    29.     if (cell == nil) {
    30.         cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
    31.     }
    32.     Event *event = (Event *)[eventsArray objectAtIndex:indexPath.row];
    33.     cell.textLabel.text = [dateFormatter stringFromDate:[event creationDate]];
    34.     NSString *string = [NSString stringWithFormat:@"%@, %@",
    35.                         [numberFormatter stringFromNumber:[event latitude]],
    36.                         [numberFormatter stringFromNumber:[event longitude]]];
    37.     cell.detailTextLabel.text = string;
    38.     return cell;
    39. }
  4. 還要實現一個getManagedObjectContext的方法: [cpp] view plaincopy

    1. - (NSManagedObjectContext *)getManagedObjectContext {
    2.     NSManagedObjectContext *context = nil;
    3.     id delegate = [[UIApplication sharedApplication] delegate];
    4.     if ([delegate performSelector:@selector(managedObjectContext)]) {
    5.         context = [delegate managedObjectContext];
    6.     }
    7.     return context;
    8. }
  5. 最後在viewDidLoad方法最末尾加上eventArray和context的初始化代碼: [cpp] view plaincopy

    1. eventsArray = [[NSMutableArray alloc] init];
    2. managedObjectContext = self.getManagedObjectContext;
  6. 運行程序,添加event看看效果。

Fetching Events

獲取數據的步驟比較簡單:

  1. 創建請求;
  2. 設置排序條件(可選);
  3. 執行查詢;
  4. 將數據放到我們准備好的(可變)數組中;

具體代碼如下,我們需要創建一個loadData方法,然後在viewDidLoad中執行loadData:

[cpp] view plaincopy

  1. -(void)loadData
  2. {
  3.     // Create the Request
  4.     NSFetchRequest *request = [[NSFetchRequest alloc] init];
  5.     NSEntityDescription *entity = [NSEntityDescription entityForName:@"Event" inManagedObjectContext:managedObjectContext];
  6.     [request setEntity:entity];
  7.     // Set the Sort Descriptor
  8.     NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"creationDate" ascending:NO];
  9.     NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
  10.     [request setSortDescriptors:sortDescriptors];
  11.     // Execute the Request
  12.     NSError *error = nil;
  13.     NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
  14.     if (mutableFetchResults == nil) {
  15.         // Handle the error.
  16.         NSLog(@"error loading data");
  17.     }
  18.     // Finish Up
  19.     [self setEventsArray:mutableFetchResults];
  20. }

 

Deleting Events

刪除操作主要是覆蓋commitEditingStyle方法,刪除Object、刪除界面中的顯示,代碼如下:

[cpp] view plaincopy

  1. - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
  2. {
  3.     if (editingStyle == UITableViewCellEditingStyleDelete) {
  4.         // Delete the managed object at the given index path.
  5.         NSManagedObject *eventToDelete = [eventsArray objectAtIndex:indexPath.row];
  6.         [managedObjectContext deleteObject:eventToDelete];
  7.         // Update the array and table view.
  8.         [eventsArray removeObjectAtIndex:indexPath.row];
  9.         [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES];
  10.         // Commit the change.
  11.         NSError *error = nil;
  12.         if (![managedObjectContext save:&error]) {
  13.             // Handle the error.
  14.         }
  15.     }
  16.     else if (editingStyle == UITableViewCellEditingStyleInsert) {
  17.         // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
  18.     }
  19. }

 

至此,這個例子完成,運行即可查看效果。

 

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved