CoreData之NSFetchedResultsController

看了一段時(shí)間的CoreData Codes,隨著涉獵越多,便越發(fā)覺(jué)得CoreData設(shè)計(jì)之精妙。今日在此分享CoreData之NSFetchedResultsController的妙用。


簡(jiǎn)介

NSFetchedResultsController的設(shè)計(jì)初衷是為了在UITableview中高效地管理那些從Core Data中fetch得到的數(shù)據(jù)。UITableView使用fetch request得到的數(shù)據(jù)來(lái)配置table cell,當(dāng)Core Data中的數(shù)據(jù)產(chǎn)生變化時(shí),NSFetchedResultsController能夠有效地對(duì)得到的結(jié)果進(jìn)行分析,并對(duì)UITableView進(jìn)行調(diào)整。

當(dāng)Core Data中數(shù)據(jù)產(chǎn)生變化,使得UITableView中展示的內(nèi)容需要更新時(shí),NSFetchedResultsControllerDelegate能夠幫助開(kāi)發(fā)者大大減少代碼量,節(jié)省不少時(shí)間。


TapList-Start

下面,就一個(gè)簡(jiǎn)單的例子CoreData-TapList,來(lái)看下NSFetchedResultsController的強(qiáng)大力量。

下載并打開(kāi)工程,首先來(lái)看TapList的Data Model:


data model

該Model中只有一個(gè)Entity,名為Item。每個(gè)item有3個(gè)屬性,其中name代表名字,score是代表分?jǐn)?shù),image代表這個(gè)item的圖片。

table view中的一個(gè)cell表示一個(gè)item,cell布局如下:


table cell

table view把Core Data中存儲(chǔ)的所有item按score從高到低的順序排列。當(dāng)點(diǎn)擊某個(gè)cell的時(shí)候,該cell對(duì)應(yīng)的item的score就加1,table view要根據(jù)最新的數(shù)據(jù)對(duì)cell進(jìn)行實(shí)時(shí)更新。

以上就是TapList這個(gè)小應(yīng)用的主體了。如果不用NSFetchedResultsController,該應(yīng)用將在Core Data中數(shù)據(jù)有變化的時(shí)候調(diào)用reloadTable方法來(lái)實(shí)時(shí)更新顯示界面。接下來(lái),我將在此基礎(chǔ)上介紹引入NSFetchedResultsController的實(shí)現(xiàn)方式,你可以看到NSFetchedResultsController帶來(lái)的便利。


TapList-NSFetchedResultsController

在原代碼中將updateTableView方法和tableData全部刪去。雖然這會(huì)導(dǎo)致Xcode報(bào)出不少bug出來(lái),但沒(méi)關(guān)系,慢慢來(lái)。

1.在ViewController中添加一個(gè)NSFetchedResultsController屬性:

var fetchedResultsController: NSFetchedResultsController!

2.在viewDidLoad中,修改代碼如下:

override func viewDidLoad() {
    super.viewDidLoad()
    
    self.tableView.estimatedRowHeight = CGFloat(76)
    self.tableView.rowHeight = CGFloat(76)
    
    let fetchRequest = NSFetchRequest(entityName: "Item")
    let sortDescriptor = NSSortDescriptor(key: "score", ascending: false)
    fetchRequest.sortDescriptors = [sortDescriptor]
    
    fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.context, sectionNameKeyPath: nil, cacheName: nil)
    
    do {
        try fetchedResultsController.performFetch()
    } catch let error as NSError {
        print("Error: \(error.userInfo)")
    }
}

首先構(gòu)建一個(gè)fetchRequest,這時(shí)必須指定fetchRequest的sortDescriptors,因?yàn)閠able view必須按某個(gè)順序來(lái)排列一個(gè)個(gè)cell。

接下來(lái),NSFetchedResultsController使用上一步構(gòu)造的fetchRequest來(lái)初始化自己。

緊接著,fetchedResultsController調(diào)用performFetch方法來(lái)從Core Data中取出數(shù)據(jù)。調(diào)用這個(gè)方法后,不需要reloadTable了,因?yàn)閒etchedResultsController會(huì)根據(jù)你所定義的UITableViewDataSource相關(guān)方法,來(lái)替你配置cell并更新UI。

3.在UITableViewDataSource相關(guān)方法中,實(shí)現(xiàn)如下:

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return fetchedResultsController.fetchedObjects!.count
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("ItemCell", forIndexPath: indexPath) as! ItemCell
    
    cell.item = fetchedResultsController.objectAtIndexPath(indexPath) as? Item

    return cell
}

其中,fetchedResultsController.fetchedObjects記錄了所有fetch得到的item,能根據(jù)其數(shù)目確定cell的數(shù)量,并配置相應(yīng)cell。

4.在didSelectRowAtIndexPath中更新如下:

let item = fetchedResultsController.objectAtIndexPath(indexPath) as! Item

5.extension ViewController,使其conform NSFetchedResultsControllerDelegate 協(xié)議:

extension ViewController: NSFetchedResultsControllerDelegate {
    func controllerWillChangeContent(controller: NSFetchedResultsController) {
        self.tableView.beginUpdates()
    }
    
    func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
        switch type {
        case .Insert:
            tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Automatic)
        case .Delete:
            tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Automatic)
        case .Update:
            let cell = tableView.cellForRowAtIndexPath(indexPath!) as! ItemCell
            cell.item = fetchedResultsController.objectAtIndexPath(indexPath!) as? Item
        case .Move:
            tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Automatic)
            tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Automatic)
        }
    }
    
    func controllerDidChangeContent(controller: NSFetchedResultsController) {
        self.tableView.endUpdates()
    }
}

這部分代碼是使NSFetchedResultsController發(fā)揮作用的關(guān)鍵。主要過(guò)程分為以下3步:
1.在controller的content即將改變的時(shí)候,允許tableView進(jìn)行更新。
2.對(duì)object的各種改變進(jìn)行相應(yīng)處理。其中anObject是發(fā)生變化的物體,indexPath指向改變前,newIndexPath指向改變后。
3.在controller的content改變即將完成時(shí),使tableView結(jié)束更新。

6.最后,別忘了在viewDidLoad設(shè)置fetchedResultsController的delegate:

fetchedResultsController.delegate = self

7.重新運(yùn)行該app,會(huì)發(fā)現(xiàn)該app不僅能正常運(yùn)行,記錄并實(shí)時(shí)更新item的信息,而且多了一些動(dòng)畫(huà)效果。這也是NSFetchedResultsController帶來(lái)的Bonus之一。


結(jié)語(yǔ)

最終Demo已經(jīng)上傳到這里,希望這篇文章對(duì)你有所幫助_。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 上篇文章寫(xiě)了數(shù)據(jù)的操作,今天簡(jiǎn)單的介紹NSFetchedResultsController的使用,文中不足之處還請(qǐng)...
    章魚(yú)卷閱讀 545評(píng)論 0 1
  • *面試心聲:其實(shí)這些題本人都沒(méi)怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個(gè)offer,總結(jié)起來(lái)就是把...
    Dove_iOS閱讀 27,666評(píng)論 30 472
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,755評(píng)論 4 61
  • 無(wú)聊而寂靜的日子總會(huì)讓我沉淀得不想個(gè)年少姑娘
    離雨閱讀 333評(píng)論 0 0
  • 今年的冬天來(lái)得格外的遲,二月底了,寒假結(jié)束,已經(jīng)準(zhǔn)備開(kāi)學(xué),寒冷才吞噬了我的骨髓。 終于還是開(kāi)學(xué)了,新年...
    一個(gè)啾咪閱讀 161評(píng)論 0 0

友情鏈接更多精彩內(nèi)容