2.0 連接模型到視圖 (Core Data Programming Guide翻譯)

這是蘋果官方文檔 Core Data Programming Guide 的渣翻譯。

在OS X系統(tǒng)中,Core Data設(shè)計(jì)目的在于通過Cocoa bindings在用戶接口發(fā)揮作用妓蛮。然而味悄,Cocoa bindings并不是iOS用戶接口的一部分荷憋。在iOS中肋演,你需要使用NSFetchedResultsController去連接模型(Core Data)和視圖(storyboard)术唬。

NSFetchedResultsController提供了Core Data和UITableView對(duì)象之間的接口薪伏。因?yàn)閠able視圖是在iOS中用得最多的用以展示數(shù)據(jù)的方式,UITableView幾乎處理了所有大量數(shù)據(jù)集合的顯示粗仓。

創(chuàng)建一個(gè)Fetched Result Controller

一般來講嫁怀,一個(gè)NSFetchedResultsController實(shí)例會(huì)被一個(gè)UITableViewController實(shí)例在需要的時(shí)候初始化。這個(gè)初始化過程可以發(fā)生在viewDidLoad或者viewWillAppear方法借浊,或者在其他視圖控制器任意一個(gè)生命周期中某個(gè)時(shí)間點(diǎn)塘淑。下面這個(gè)例子展示了NSFetchedResultsController的初始化。

OBJECTIVE-C

@property (nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
 
- (void)initializeFetchedResultsController
{
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Person"];
 
    NSSortDescriptor *lastNameSort = [NSSortDescriptor sortDescriptorWithKey:@"lastName" ascending:YES];
 
    [request setSortDescriptors:@[lastNameSort]];
 
    NSManagedObjectContext *moc = …; //Retrieve the main queue NSManagedObjectContext
 
    [self setFetchedResultsController:[[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:moc sectionNameKeyPath:nil cacheName:nil]];
    [[self fetchedResultsController] setDelegate:self];
 
    NSError *error = nil;
    if (![[self fetchedResultsController] performFetch:&error]) {
        NSLog(@"Failed to initialize FetchedResultsController: %@\n%@", [error localizedDescription], [error userInfo]);
        abort();
    }
}

SWIFT

var fetchedResultsController: NSFetchedResultsController!
 
func initializeFetchedResultsController() {
    let request = NSFetchRequest(entityName: "Person")
    let departmentSort = NSSortDescriptor(key: "department.name", ascending: true)
    let lastNameSort = NSSortDescriptor(key: "lastName", ascending: true)
    request.sortDescriptors = [departmentSort, lastNameSort]
    
    let moc = self.dataController.managedObjectContext
    fetchedResultsController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: moc, sectionNameKeyPath: "department.name", cacheName: "rootCache")
    fetchedResultsController.delegate = self
    
    do {
        try fetchedResultsController.performFetch()
    } catch {
        fatalError("Failed to initialize FetchedResultsController: \(error)")
    }
}

在上述的蚂斤、在UITableViewController實(shí)例運(yùn)行的時(shí)候能一直存活的initializeFetchedResultsController方法中存捺,你第一步構(gòu)建了一個(gè)Fetch請(qǐng)求(NSFetchRequest),F(xiàn)etch請(qǐng)求是這個(gè)NSFetchedResultsController的中心曙蒸。注意這個(gè)Fetch請(qǐng)求包含了一個(gè)排序描述器(NSSortDescriptor)捌治。NSFetchedResultsController需要至少一個(gè)排序描述器來控制展示出來的數(shù)據(jù)順序。

一旦Fetch請(qǐng)求被初始化纽窟,你就可以初始化NSFetchedResultsController實(shí)例了肖油。Fetched Results Controller需要你傳遞一個(gè)NSFetchRequest實(shí)例和一個(gè)托管對(duì)象上下文實(shí)例(NSManagedObjectContext)來運(yùn)行操作。sectionNameKeyPath和cacheName屬性都是可選的臂港。

一旦Fetched Results Controller初始化了森枪,你就可以設(shè)置它的代理(delegate)。這個(gè)代理會(huì)通知table view有關(guān)任何的數(shù)據(jù)結(jié)構(gòu)的變化审孽。一般來說县袱,table view同時(shí)也是Fetched Results Controller的代理,所以能夠在相關(guān)數(shù)據(jù)發(fā)生變化的時(shí)候調(diào)用回調(diào)佑力。

下一步式散,你可以開始調(diào)用performFetch:來使用NSFetchedResultsController。這個(gè)調(diào)用會(huì)查詢初始數(shù)據(jù)用以展示打颤,并觸發(fā)NSFetchedResultsController實(shí)例開始監(jiān)控托管對(duì)象上下文MOC的變化杂数。

集成Fetched Results Controller 和 Table View Data Source

在你集成了已初始化的fetched results controller和準(zhǔn)備好了要展示在table view上的數(shù)據(jù)之后,你需要集成fetched results controller和table view的data source(UITableViewDataSource)瘸洛。

OBJECTIVE-C

#pragma mark - UITableViewDataSource
 
- (void)configureCell:(id)cell atIndexPath:(NSIndexPath*)indexPath
{
    id object = [[self fetchedResultsController] objectAtIndexPath:indexPath];
 
    // Populate cell from the NSManagedObject instance
}
 
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    id cell = [tableView dequeueReusableCellWithIdentifier:CellReuseIdentifier];
    // Set up the cell
    [self configureCell:cell atIndexPath:indexPath];
    return cell;
}
 
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return [[[self fetchedResultsController] sections] count];
}
 
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    id< NSFetchedResultsSectionInfo> sectionInfo = [[self fetchedResultsController] sections][section];
    return [sectionInfo numberOfObjects];
}

SWIFT

func configureCell(cell: UITableViewCell, indexPath: NSIndexPath) {
    let employee = fetchedResultsController.objectAtIndexPath(indexPath) as! AAAEmployeeMO
    // Populate cell from the NSManagedObject instance
    print("Object for configuration: \(object)")
}
 
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("cellIdentifier", forIndexPath: indexPath) as! UITableViewCell
    // Set up the cell
    configureCell(cell, indexPath: indexPath)
    return cell
}
 
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return fetchedResultsController.sections!.count
}
 
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    let sections = fetchedResultsController.sections as! [NSFetchedResultsSectionInfo]
    let sectionInfo = sections[section]
    return sectionInfo.numberOfObjects
}

就如上面每個(gè)UITableViewDataSource的方法展示的那樣揍移,fetched results controller的集成接口減少到了只需要一個(gè)就能跟table view data source進(jìn)行集成了。

傳遞數(shù)據(jù)更新到Table View

除了更加容易集成Core Data和table view data source之外反肋,NSFetchedResultsController還能在數(shù)據(jù)發(fā)生改變的時(shí)候跟UITableViewController實(shí)例通信那伐。為了實(shí)現(xiàn)這個(gè)功能,需要實(shí)現(xiàn) NSFetchedResultsControllerDelegate協(xié)議:

OBJECTIVE-C

#pragma mark - NSFetchedResultsControllerDelegate
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
    [[self tableView] beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
    switch(type) {
        case NSFetchedResultsChangeInsert:
            [[self tableView] insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;
        case NSFetchedResultsChangeDelete:
            [[self tableView] deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;
        case NSFetchedResultsChangeMove:
        case NSFetchedResultsChangeUpdate:
            break;
    }
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath
{
    switch(type) {
        case NSFetchedResultsChangeInsert:
            [[self tableView] insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
        case NSFetchedResultsChangeDelete:
            [[self tableView] deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
        case NSFetchedResultsChangeUpdate:
            [self configureCell:[[self tableView] cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            break;
        case NSFetchedResultsChangeMove:
            [[self tableView] deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
            [[self tableView] insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
    [[self tableView] endUpdates];
}

SWIFT

func controllerWillChangeContent(controller: NSFetchedResultsController) {
    tableView.beginUpdates()
}
 
func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {
    switch type {
    case .Insert:
        tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade)
    case .Delete:
        tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade)
    case .Move:
        break
    case .Update:
        break
    }
}
 
func controller(controller: NSFetchedResultsController, didChangeObject anObject: NSManagedObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
    switch type {
    case .Insert:
        tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade)
    case .Delete:
        tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
    case .Update:
        configureCell(self.tableView.cellForRowAtIndexPath(indexPath!)!, indexPath: indexPath!)
    case .Move:
        tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
        tableView.insertRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
    }
}
 
func controllerDidChangeContent(controller: NSFetchedResultsController) {
    tableView.endUpdates()
}

實(shí)現(xiàn)這4個(gè)上述的協(xié)議方法就能夠自動(dòng)更新UITableView,無論何時(shí)相關(guān)數(shù)據(jù)的更新都能夠觸發(fā)更新罕邀。

添加Sections

到目前為止你可以已經(jīng)使用一個(gè)僅有一個(gè)sectiion的table view畅形,這個(gè)setion展示了所有table view需要展示的數(shù)據(jù)。如果你要操作擁有更多數(shù)據(jù)的Employee對(duì)象集合诉探,那么把這個(gè)table view分成多個(gè)section更好日熬。把Employee按照department來分組更加利于管理。如果不使用Core Data肾胯,實(shí)現(xiàn)一個(gè)帶有多個(gè)section的table view可以解析一個(gè)含有多個(gè)數(shù)組的數(shù)組或更加復(fù)雜的數(shù)據(jù)結(jié)構(gòu)竖席。有了Core Data,你僅需要對(duì)fetched results controller的構(gòu)建做出一點(diǎn)小修改即可.

OBJECTIVE-C

- (void)initializeFetchedResultsController
{
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Person"];
    NSSortDescriptor *departmentSort = [NSSortDescriptor sortDescriptorWithKey:@"department.name" ascending:YES];
    NSSortDescriptor *lastNameSort = [NSSortDescriptor sortDescriptorWithKey:@"lastName" ascending:YES];
    [request setSortDescriptors:@[departmentSort, lastNameSort]];
    NSManagedObjectContext *moc = [[self dataController] managedObjectContext];
    [self setFetchedResultsController:[[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:moc sectionNameKeyPath:@"department.name" cacheName:nil]];
    [[self fetchedResultsController] setDelegate:self];

SWIFT

func initializeFetchedResultsController() {
    let request = NSFetchRequest(entityName: "Person")
    let departmentSort = NSSortDescriptor(key: "department.name", ascending: true)
    let lastNameSort = NSSortDescriptor(key: "lastName", ascending: true)
    request.sortDescriptors = [departmentSort, lastNameSort]
    let moc = dataController.managedObjectContext
    fetchedResultsController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: moc, sectionNameKeyPath: "department.name", cacheName: nil)
    fetchedResultsController.delegate = self
    do {
        try fetchedResultsController.performFetch()
    } catch {
        fatalError("Failed to initialize FetchedResultsController: \(error)")
    }
}

在這個(gè)例子中你需要添加多一個(gè)NSSortDesctiptor實(shí)例到NSFetchRequest實(shí)例中敬肚。你要在NSFetchedResultsController的初始化中為sectionNameKeyPath參數(shù)設(shè)置跟這個(gè)新sort descriptor同樣的key毕荐。fetched results controller使用這個(gè)初始化的controller并把數(shù)據(jù)分成多個(gè)section,因此所有的key都要匹配艳馒。

這個(gè)修改讓fetched results controller把返回的Person實(shí)例基于每個(gè)Person相關(guān)的department的name分成了多個(gè)section憎亚。唯一使用這個(gè)特性的條件是:

  • sectionNameKeyPath屬性必須是一個(gè)NSSortDescriptor實(shí)例。
  • 上述NSSortDescriptor必須是傳遞給fetch request的數(shù)組中的第一個(gè)descriptor弄慰。

為性能而添加的緩存

在許多情況下第美,一個(gè)table view會(huì)表示同一種相近類型的數(shù)據(jù)。一個(gè)fetch request在table view controller創(chuàng)建的時(shí)候被定義陆爽,并且在應(yīng)用的生命周期內(nèi)不會(huì)改變什往。如果能給NSFetchedResultsController實(shí)例添加一個(gè)緩存,當(dāng)應(yīng)用重新啟動(dòng)而且數(shù)據(jù)沒有改變的時(shí)候墓陈,這無疑是極好的恶守,這樣table view能迅速初始化第献。特別是對(duì)大型數(shù)據(jù)集贡必,緩存特別有用。

OBJECTIVE-C

[self setFetchedResultsController:[[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:moc sectionNameKeyPath:@"department.name" cacheName:@"rootCache"]];

SWIFT

fetchedResultsController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: moc, sectionNameKeyPath: "department.name", cacheName: "rootCache")

就如上面展示的庸毫,cacheName參數(shù)在NSFetchedResultsController實(shí)例初始化的時(shí)候設(shè)置仔拟,fetched result controller會(huì)自動(dòng)獲得這個(gè)緩存。之后數(shù)據(jù)的加載幾乎是瞬間的事飒赃。

注意  
如果一個(gè)fetched results controller相關(guān)的fetch request要發(fā)生改變利花,很重要一點(diǎn)就是在fetched results controller發(fā)生改變之前緩存必須無效化。你可以使用NSFetchedResultsController的類方法deleteCacheWithName:來無效化緩存载佳。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末炒事,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蔫慧,更是在濱河造成了極大的恐慌挠乳,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異睡扬,居然都是意外死亡盟蚣,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門卖怜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來屎开,“玉大人,你說我怎么就攤上這事马靠⊙俪椋” “怎么了?”我有些...
    開封第一講書人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵虑粥,是天一觀的道長(zhǎng)如孝。 經(jīng)常有香客問我,道長(zhǎng)娩贷,這世上最難降的妖魔是什么第晰? 我笑而不...
    開封第一講書人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮彬祖,結(jié)果婚禮上茁瘦,老公的妹妹穿的比我還像新娘。我一直安慰自己储笑,他們只是感情好甜熔,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著突倍,像睡著了一般腔稀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上羽历,一...
    開封第一講書人閱讀 52,255評(píng)論 1 308
  • 那天焊虏,我揣著相機(jī)與錄音,去河邊找鬼秕磷。 笑死诵闭,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的澎嚣。 我是一名探鬼主播疏尿,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼易桃!你這毒婦竟也來了褥琐?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤晤郑,失蹤者是張志新(化名)和其女友劉穎敌呈,沒想到半個(gè)月后嚼鹉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡驱富,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年锚赤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片褐鸥。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡线脚,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出叫榕,到底是詐尸還是另有隱情浑侥,我是刑警寧澤,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布晰绎,位于F島的核電站寓落,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏荞下。R本人自食惡果不足惜伶选,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望尖昏。 院中可真熱鬧仰税,春花似錦、人聲如沸抽诉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽迹淌。三九已至河绽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間唉窃,已是汗流浹背耙饰。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留句携,地道東北人榔幸。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓允乐,卻偏偏與公主長(zhǎng)得像矮嫉,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子牍疏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容