[Cocoa]深入淺出Cocoa之Core Data(3)- 使用綁定
深入淺出Cocoa之Core Data(3)- 使用綁定
羅朝輝 (http://www.rzrgm.cn/kesalin/)
前面講解了 Core Data 的框架,并完全手動編寫代碼演示了 Core Data 的運作過程。下面我們來演示如何結合 XCode 強大的可視化編輯以及 Cocoa 鍵值編碼,綁定機制來使用 Core Data。有了上面提到的哪些利器,在這個示例中,我們無需編寫 NSManagedObjectModel 代碼,也無需編寫 NSManagedObjectContext,工程模版在背后為我們做了這些事情。
今天要完成的這個示例,有兩個 Entity:StudentEntity 與 ClassEntity,各自有一個名為 name 的 Attribute。其中 StudentEntity 通過一個名為 inClass 的 relationship 與 ClassEntity關聯,而 ClassEntity 也有一個名為 students 的 relationship 與 StudentEntity 關聯,這是一個一對多的關系。此外 ClassEntity 還有一個名為 monitor 的 relationship 關聯到 StudentEntity,表示該班的班長。
代碼下載:點此下載
最終的效果圖如下:


下面我們一步一步來完成這個示例:
1,創建工程:
創建一個 Cocoa Application,工程名為:MacCoreData,并勾選 Create Document-Based Application 和 Use Core Data,在這里要用到 Core Data 和 Document 工程模版,以簡化代碼的編寫。

2,分類文件:
在 MacCoreData 下新建 Src 和 Res 兩個 Group,并將 MyDocument.h 和 MyDocument 拖到 Src 下,將其他 xib 和 xcdatamodeld 拖到 Res 中。將文件分類是個好習慣,尤其是對大項目來說。后面請自覺將文件分類~~

3,創建 Entity:
在工程中,我們可以看到名為 MyDocument.xcdatamodeld 的文件,其后綴表明這是一個 core data model文件,框架就是讀取該模型文件生成模型的。下面我們選中這個文件,向其中添加兩個實體。點擊下方的 Add Entity 增加兩個新 Entity: ClassEntity 和 StudentEntity。
向 StudentEntity 中添加名為 name 的 string 類型的 Attribute,并設置其 Default Value 為學生甲,去除 Optional 前勾選狀態;
向 ClassEntity 中添加名為 name 的 string 類型的 Attribute,并設置其 Default Value 為XX班,去除 Optional 前勾選狀態;
選項 Optional 是表示該 Attribute 可選與否的,在這里 name 都是必須的。

向 StudentEntity 中添加名為 inClass 指向 ClassEntity 的 Relationship,其 Inverse 欄要等 ClassEntity 添加了反向關系才能選擇,后面回提到;
向 ClassEntity 中添加名為 students 指向 StudentEntity 的 Relationship,其 Inverse 欄選擇 inClass,表明這是一個雙向關系,勾選 To-Many Relationship,因為一個班級可以有多名學生,這是一對多關系。設定之后,我們可以可以將 StudentEntity 的 inClass 關系的 Inverse 設置為 students了。
再向 ClassEntity 中添加名為 monitor 指向 StudentEntity 的 Relationship,表示該班的班長。

4,生成 NSManagedObject 類:
選中 StudentEntity,然后點擊菜單 File-> New -> New file…,添加 Core Data -> NSManagerObject subclass, XCode 就會自動為我們生成 StudentEntity.h 和 StudentEntity.m 文件,記得將這兩個文件拖放到 Src Group 下。下面我們來看看這兩個文件中有什么內容:
StudentEntity.h
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
@class ClassEntity;
@interface StudentEntity : NSManagedObject {
@private
}
@property (nonatomic, retain) NSString * name;
@property (nonatomic, retain) ClassEntity * inClass;
@end
StudentEntity.m
#import "StudentEntity.h"
#import "ClassEntity.h"
@implementation StudentEntity
@dynamic name;
@dynamic inClass;
@end
在前面手動代碼的示例中,我們是自己編寫 Run NSManagedObject的代碼,而現在,XCode 已經根據模型文件的描述,自動為我們生成了,方便吧。有時候自動生成的代碼不一定滿足我們的需要,我們就得對代碼進行修改,比如對 ClassEntity 來說,班長只能是其 students 中的一員,如果我們在 students 中移除了班長那個學生,那么該班級的班長就應該置空。
選中 ClassEntity,重復上面的步驟,自動生成 ClassEntity.h 和 ClassEntity.m,下面我們根據需求來修改 ClassEntity.m。
在 - (void)removeStudentsObject:(StudentEntity *)value 的開頭添加如下代碼:
if (value == [self monitor])
[self setMonitor:nil];
在 - (void)removeStudents:(NSSet *)value 的開頭添加如下代碼:
if ([value containsObject:[self monitor]])
[self setMonitor:nil];
這樣當我們在 students 中刪除一個學生時,就會檢測該學生是不是班長,如果是,就將該班的班長置空。
5,下面來生成 UI 界面:
在這里,我們是通過切換 view 的方法來顯現學生與班級兩個界面,因此我們需要主界面,班級以及學生共三個界面。
向 MyDocument.xib 中添加如下一個 popup button 和一個 NSBox。并刪除 popup 控件中的 menu item,因為我們要通過代碼來添加班級,學生項的。

然后在 Res 中添加兩個新 Empty xib 文件:StudentView.xib 和 ClassView.xib,分別向這兩個 xib 文件中拖入一個 Custom View,然后在這個 view 添加相關控件構成 UI。記得設置 ClassView 中兩個 tableView 的列數為 1,拖入一個 PopupButtonCell 到 StudentView 中班級那一列。效果如下:


6,添加 ViewController:
下面我們創建 ViewController 來在程序中轉載 xib 文件,顯示和切換 view。為了便于切換 view,我們創建一個繼承自 NSViewController 的名為:ManagedViewController的類(記得不要創建該類對應的 xib 文件!創建一個 NSObject子類,然后修改其父類為 NSViewController),然后讓 StudentViewController 和 ClassViewController 從它繼承。ManagedViewController 類的代碼如下:
ManagedViewController.h
#import <Cocoa/Cocoa.h>
@interface ManagedViewController : NSViewController {
@private
NSManagedObjectContext * managedObjectContext;
NSArrayController * contentArrayController;
}
@property (nonatomic, retain) NSManagedObjectContext * managedObjectContext;
@property (nonatomic, retain) IBOutlet NSArrayController *contentArrayController;
@end
ManagedViewController.m
#import "ManagedViewController.h"
@implementation ManagedViewController
@synthesize managedObjectContext;
@synthesize contentArrayController;
- (void)dealloc
{
self.contentArrayController = nil;
self.managedObjectContext = nil;
[super dealloc];
}
// deal with "Delete" key event.
//
- (void) keyDown:(NSEvent *)theEvent
{
if (contentArrayController) {
if ([theEvent keyCode] == 51) {
[contentArrayController remove:nil];
}
else {
[super keyDown:theEvent];
}
}
else {
[super keyDown:theEvent];
}
}
@end
在上面代碼中,我們有一個 NSManagedObjectContext * managedObjectContext 指針,它指向 MyDocument 框架中的NSManagedObjectContext對象,后面我們會說到,至于 NSArrayController * contentArrayController,它是一個 IBOutlet,將與xib 中創建的 NSArrayController關聯,后面也會說到。在這里引入 contentArrayController 是為了讓 delete 能夠刪除記錄。
ClassViewController 類的代碼如下:
#import "ManagedViewController.h"
@interface ClassViewController : ManagedViewController {
@private
}
@end
#import "ClassViewController.h"
@implementation ClassViewController
- (id)init
{
self = [super initWithNibName:@"ClassView" bundle:nil];
if (self) {
[self setTitle:@"班級"];
}
return self;
}
- (void)dealloc
{
[super dealloc];
}
@end
StudentViewController 類的代碼如下:
#import "ManagedViewController.h"
@interface StudentViewController : ManagedViewController {
@private
}
@end
#import "StudentViewController.h"
@implementation StudentViewController
- (id)init
{
self = [super initWithNibName:@"StudentView" bundle:nil];
if (self) {
[self setTitle:@"學生"];
}
return self;
}
- (void)dealloc
{
[super dealloc];
}
@end
在這兩個子類中,我們在 init 方法中載入 xib 文件,然后設置其 title。
===前半部分完,請接著看后半部分===
浙公網安備 33010602011771號