本篇主要講述一些細枝末節的知識點。
建模
屬性和關系
在XCode中,建模時就可以添加屬性和標識實體間的相互關系。具體參見下圖,下面還有對應說明:
- Entities中列舉了實體的列表;
- 每個Entity的屬性列表在Attributes中,注意Attributes可以多選之後在右邊一起改屬性類型;
- 通過添加關系可以標識出兩種不同類型的Entity之間的相互聯系;
- 每個Relationship都是可以編輯的,主要側重於一對一還是多對一的關系描述;在我們這個例子中,我們在Employer中設置一對一的關系,然後在Company中選擇"To-Many Relationship"選項,使得Company與Employer變成一對多的關系;
- 編輯完相互關系後,在Inverse中設置一下,使兩者的關系變成真正的關聯關系,而不是孤立的聯系(這個可以選擇Editor Style為table看到兩者區別),到這裡是不是和Hibernate有點類似了;
創建NSManagedObject類的子類
好處——類型校驗
創建NSManagedObject類的子類的對象的好處是你可以不用如下的代碼(很明顯如下的代碼是沒有類型校驗的):
[cpp] view plaincopy
- NSManagedObject *event = [NSEntityDescription insertNewObjectForEntityForName:@"Event" inManagedObjectContext:managedObjectContext];
- [event setValue:[NSDate date] forKey:@"creationDate"];
步驟
- 選中.xcdatamodeld這個模型文件;
- 選中一個實體;
- 從菜單中選擇Editor->Create NSManagedObject class,可以創建需要的子類;
取回數據
注意fetch data的時候實際上可以直接使用code snippet,如下:
使用斷言
使用斷言可以用來篩選我們取出的數據,所以我們的代碼可能是這樣,同樣可以使用code snippet:
[cpp] view plaincopy
- -(void)loadData
- {
- // Create the Request
- NSFetchRequest *request = [[NSFetchRequest alloc] init];
- NSEntityDescription *entity = [NSEntityDescription entityForName:@"Event" inManagedObjectContext:managedObjectContext];
- [request setEntity:entity];
- // Set the Sort Descriptor
- NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"creationDate" ascending:NO];
- NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
- [request setSortDescriptors:sortDescriptors];
- // Create Predicate
- // equal
- // NSPredicate *predicate = [NSPredicate predicateWithFormat:@"description == 'Tom's birthday'"];
- // NSPredicate *predicate = [NSPredicate predicateWithFormat:@"description == [c]'tom's birthday'"];
- // like
- // NSPredicate *predicate = [NSPredicate predicateWithFormat:@"description LIKE 'Tom*'"];
- // NSPredicate *predicate = [NSPredicate predicateWithFormat:@"description LIKE [cd]'Tom*'"];
- // NSPredicate *predicate = [NSPredicate predicateWithFormat:@"description LIKE '*om*'"];
- // begins
- // NSPredicate *predicate = [NSPredicate predicateWithFormat:@"description BEGINSWITH 'TOM'"];
- // date
- // NSPredicate *predicate = [NSPredicate predicateWithFormat:@"createDate < %@", [NSDate date]];
- // and
- NSPredicate *predicate = [NSPredicate predicateWithFormat:@"description BEGINSWITH 'TOM' AND createDate < %@", [NSDate date]];
- [request setPredicate:predicate];
- // Execute the Request
- NSError *error = nil;
- NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
- if (mutableFetchResults == nil) {
- // Handle the error.
- NSLog(@"error loading data");
- }
- // Finish Up
- [self setEventsArray:mutableFetchResults];
- }
創建帶斷言的fetching request templete
創建模板的好處是XCode本身提供了一個編輯器,這樣就不需要我們自己編寫這部分斷言代碼,而是可以使用圖形化的方式編輯我們的條件,而且便於我們管理這部分代碼。
- 我們選擇模型文件,然後去菜單,選擇Editor->Add Fetch Request:
- 進入之後的頁面中我們可以編輯我們的條件,並且可以切換圖形和代碼方式查看:
- 編輯完條件後,我們的代碼變更為:
[cpp] view plaincopy
- // Create Fetch Request
- NSFetchRequest *fetchRequest = [[self managedObjectModel] fetchRequestTemplateForName:@"EssentialCourses"];
- // Create Sort Descriptor
- NSSortDescriptor *sort = [[NSSortDescriptor alloc]initWithKey:@"releaseDate" ascending:YES];
- NSArray *sortDescriptors = [[NSArray alloc]initWithObjects:sort, nil];
- [fetchRequest setSortDescriptors:sortDescriptors];
- NSError *error = nil;
- NSArray *fetchedObjects = [[self managedObjectContext] executeFetchRequest:fetchRequest error:&error];
- if (fetchedObjects == nil) {
- NSLog(@"Problem! %@",error);
- }
- for (Course *c in fetchedObjects) {
- NSLog(@"Course: %@ by author: %@", c.title, c.author);
- }
- 運行,查看效果。
版本管理
數據不能兼容
對於數據不能兼容的情況,我們在AppDelegate中做如下處理:
[cpp] view plaincopy
- - (NSPersistentStoreCoordinator *)persistentStoreCoordinator
- {
- if (_persistentStoreCoordinator != nil) {
- return _persistentStoreCoordinator;
- }
- NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"CoreDataCourse.sqlite"];
- NSError *error = nil;
- _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
- if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
- // delete record
- [[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil];
- NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
- abort();
- }
- return _persistentStoreCoordinator;
- }
注意其中的removeItemAtURL。
向前兼容
為了數據能夠向前兼容老版本,你需要做如下工作:
- 創建實體的新版本;
- 新實體創建後會出現如下圖的對應model文件:
- 去編輯新的實體;
- 選中.cxdatemodelId,在右邊的屬性中選擇激活的模型版本;
- 修改代碼,注意新增加的options:
[cpp] view plaincopy
- - (NSPersistentStoreCoordinator *)persistentStoreCoordinator
- {
- if (_persistentStoreCoordinator != nil) {
- return _persistentStoreCoordinator;
- }
- NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"CoreDataCourse.sqlite"];
- NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES],NSInferMappingModelAutomaticallyOption, nil];
- NSError *error = nil;
- _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
- if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
- NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
- abort();
- }
- return _persistentStoreCoordinator;
- }
- 這裡還是描述的簡單情況,實際的版本管理和數據合並更加復雜,您可能需要參考:https://developer.apple.com/library/ios/documentation/cocoa/Conceptual/CoreDataVersioning/Articles/Introduction.html
尾聲
到這裡基本上Core Data相關的功能已經介紹完了,整體上看下來Core Data的學習曲線還是蠻陡峭的,但是基本的使用非常簡單,建議大家在工作中逐步摸索。
下面是我學習的一個完整小例子供大家參考:下載地址。