IOS中Core Data的整合(Integrating Core Data with iOS)


把模型鏈接到視圖(Connecting the Model to Views)

在macOS上,Core Data與用戶界面被設(shè)計(jì)為通過Cocoa綁定工作蛤高。然后,Cocoa綁定在iOS上并不是用戶界面的一部分。在IOS中通過NSFetchedResultsController將模型(Core Data)與視圖(storyboards)鏈接在一起钓株。

創(chuàng)建一個(gè)結(jié)果控制器(Creating a Fetched Results Controller)

當(dāng)你使用一個(gè)基于表格視圖(UITableView)布局的Core Data時(shí),NSFetchedResultsController為你提供的數(shù)據(jù)通常由UItableViewController的實(shí)例將要使用時(shí)初始化陌僵。這個(gè)初始化可以在viewDidLoad或viewWillAppear:方法里轴合,再或者在視圖控制器的生命周期的另一個(gè)邏輯起點(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 = dataController.managedObjectContext
    fetchedResultsController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: moc, sectionNameKeyPath: nil, cacheName: nil)
    fetchedResultsController.delegate = self

上面所示的初始化(initiallizeFetchedResultsController)方法在UITableViewController的實(shí)例里面拾弃,首先構(gòu)建一個(gè)讀取請(qǐng)求(NSFetchRequest)值桩,這是NSFetchRequestController的核心。注意豪椿,讀取請(qǐng)求含有一種特殊的描述符(NSSortDescriptor)奔坟。NSFetchRequestController至少需要一種描述符來控制數(shù)據(jù)顯示的順序。

在初始化讀取請(qǐng)求之后搭盾,你可以初始化一個(gè)NSFetchedResultsController的實(shí)例咳秉。獲取結(jié)果控制器(fetched results controller)在運(yùn)行之前,你需要一個(gè)NSFetchRequest的實(shí)例和一個(gè)托管對(duì)象上下文(NSManagedObjectContext)的參考鸯隅。sectionNameKeyPaht和cacheName屬性都是可選的澜建。

在獲取結(jié)果控制器(fetched results controller)初始化之后,將其分派給委托蝌以。當(dāng)數(shù)據(jù)發(fā)生變化時(shí)代理通知表格視圖控制器(UITableViewController)炕舵。通常情況下,表格視圖控制器(UITableViewController)也通過代理告知獲取結(jié)果控制器(fetched results controller)跟畅,以便任何時(shí)候當(dāng)數(shù)據(jù)發(fā)生改變時(shí)接收到回調(diào)咽筋。

接下來,開始通過調(diào)用NSFetchedRrsuletsController的performFetfch:方法徊件,這個(gè)調(diào)用檢索要顯示的初始數(shù)據(jù)奸攻,并且作為NSFetchedResultsController實(shí)例開始監(jiān)測(cè)托管上下文(managed object context)開始(原因)蒜危。

將獲取到的結(jié)果與表格視圖的數(shù)據(jù)源集成(Integrating the Fetched Results Controller with the Table View Data Source)

當(dāng)你在表格視圖里初始化獲取結(jié)果控制器(initialized fetched results controller)并且有了可以用于顯示的數(shù)據(jù)之后,你就可以把獲取結(jié)果的控制器(fetched results controller)和表格視圖的數(shù)據(jù)源相互整合(UITabaleViewDataSource)睹耐。

Objective-C
#pragma mark - UITableViewDataSource
 
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    id cell = [tableView dequeueReusableCellWithIdentifier:CellReuseIdentifier];
 
    NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
    // Configure the cell from the object
    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
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let cell = tableView.dequeueReusableCell(withIdentifier: "cellIdentifier", for: indexPath) else {
        fatalError("Wrong cell type dequeued")
    }
    // Set up the cell
    guard let object = self.fetchedResultsController?.object(at: indexPath) else {
        fatalError("Attempt to configure cell without a managed object")
    }
    //Populate the cell from the object
    return cell
}
 
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return fetchedResultsController.sections!.count
}
 
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    guard let sections = fetchedResultsController.sections else {
        fatalError("No sections in fetchedResultsController")
    }
    let sectionInfo = sections[section]
    return sectionInfo.numberOfObjects
}

每個(gè)UITableViewDataSource的方法如上圖所示辐赞,與獲取結(jié)果控制器(fetched results controller)的集成是減少到一個(gè)單一的方法調(diào)用,是專門設(shè)計(jì)的表格視圖數(shù)據(jù)源整合硝训。

將數(shù)據(jù)更改與表格視圖通信(Communicationg Data Chagnes to the Table View)

除了更容易的整合Core Date和表格視圖數(shù)據(jù)源(UITableViewDataSource)响委,NSFetchResultsController在數(shù)據(jù)發(fā)生變化是還處理和表格視圖控制器(UITableViewController)的通信。通過實(shí)現(xiàn)NSFetchedResultsControllerDelegae協(xié)議來實(shí)施捎迫。

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.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
            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<NSFetchRequestResult>) {
    tableView.beginUpdates()
}
 
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
    switch type {
    case .insert:
        tableView.insertSections(IndexSet(integer: sectionIndex), with: .fade)
    case .delete:
        tableView.deleteSections(IndexSet(integer: sectionIndex), with: .fade)
    case .move:
        break
    case .update:
        break
    }
}
 
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
    switch type {
    case .insert:
        tableView.insertRows(at: [newIndexPath!], with: .fade)
    case .delete:
        tableView.deleteRows(at: [indexPath!], with: .fade)
    case .update:
        tableView.reloadRows(at: [indexPath!], with: .fade)
    case .move:
        tableView.moveRow(at: indexPath!, to: newIndexPath!)
    }
}
 
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    tableView.endUpdates()
}

上面的4個(gè)協(xié)議提供了當(dāng)?shù)讓訑?shù)據(jù)變化時(shí)自動(dòng)更新相關(guān)表格的方法晃酒。

添加的部分(Adding Sections)

到目前位置,一直使用的表格視圖都是只有一個(gè)seciton的單一表格窄绒,只有一個(gè)section的表格需要所有的數(shù)據(jù)用以展示(或者說只能展示一個(gè)真題的數(shù)據(jù))贝次。(接著前面的例子)如果你正在使用大量的員工對(duì)象數(shù)據(jù),那么將表格分成多個(gè)部分是非常有利的彰导。通過部門對(duì)員工進(jìn)行分組使員工名單更加易于管理蛔翅。如果沒有Core Data,一個(gè)具有多個(gè)部分的表格視圖將涉及數(shù)組嵌套數(shù)組的結(jié)構(gòu)位谋,或者更為復(fù)雜的數(shù)據(jù)結(jié)構(gòu)山析。使用Core Data可以簡(jiǎn)單方便的更改獲取結(jié)果控制器(fetched results controller)以達(dá)到目的。

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è)NSSortDescriptor實(shí)例到NSFetchRequest實(shí)例掏父。把新的描述符SectionNameKeyPath作為相同的密匙來初始化NSFetchedResultsController笋轨。獲取結(jié)果控制器(fetched results controller)用這些將控制器(controller)初始化用來打斷并且分類排序成多個(gè)sections,因此要求請(qǐng)求密匙必須匹配赊淑。

這個(gè)更改會(huì)讓獲取結(jié)果控制器(fetched results controller)根據(jù)每個(gè)實(shí)例關(guān)聯(lián)的部門名稱將返回的人員實(shí)例分成多個(gè)部分爵政。使用此功能的條件是:

  • sectionNameKeyPath 的屬性也必須是一個(gè)NSSortDescriptor的實(shí)例。(The sectionNameKeyPath property must also be an NSSortDescriptor instance)
  • NSSortDescriptor在請(qǐng)求數(shù)組中必須是第一個(gè)描述符陶缺。(The NSSortDescriptor must be the first descriptor in the array passed to the fetch request)
為性能添加緩存(Adding Caching for Performance)

在許多情況下钾挟,表格視圖(tableView)表示相對(duì)靜態(tài)的數(shù)據(jù)類型。在表格視圖控制器中定義了一個(gè)獲取請(qǐng)求饱岸,它在整個(gè)應(yīng)用程序的生命周期中不會(huì)改變掺出。在這種情況下,它有利于增加緩存(cache)的NSFetchedResultsController實(shí)例苫费,以便于在數(shù)據(jù)沒有改變的時(shí)候應(yīng)用程序一遍又一遍的啟動(dòng)所引起的表格視圖的突然初始化汤锨。緩存對(duì)于顯示異常大的數(shù)據(jù)時(shí)非常有用。

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")

如上圖所示百框,當(dāng)NSFetchedResultsController實(shí)例初始化設(shè)置cacheName屬性的時(shí)候泥畅,獲取結(jié)果的控制器(fetched results controller)自動(dòng)獲得緩存增益,隨后加載的數(shù)據(jù)幾乎瞬間完成。


如果發(fā)生請(qǐng)求伴隨著獲取結(jié)果控制器(fetched results controller)需要改變的情況位仁,那么在更改獲取控制器(fetched results controller)之前使緩存失效是至關(guān)重要的。你可以通過調(diào)用deleteCacheWithName:的方法使緩存(cache)失效方椎,這是一個(gè)類級(jí)別的方法在NSFetchedResultsController里聂抢。
原文:If a situation occurs where the fetch request associated with a fetched results controller needs to change, then it is vital that the cache be invalidated prior to changing the fetched results controller. You invalidate the cache by calling deleteCacheWithName:, which is a class level method on NSFetchedResultsController.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市棠众,隨后出現(xiàn)的幾起案子琳疏,更是在濱河造成了極大的恐慌,老刑警劉巖闸拿,帶你破解...
    沈念sama閱讀 221,548評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件空盼,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡新荤,警方通過查閱死者的電腦和手機(jī)揽趾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來苛骨,“玉大人篱瞎,你說我怎么就攤上這事⊙髦ィ” “怎么了俐筋?”我有些...
    開封第一講書人閱讀 167,990評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)严衬。 經(jīng)常有香客問我澄者,道長(zhǎng),這世上最難降的妖魔是什么请琳? 我笑而不...
    開封第一講書人閱讀 59,618評(píng)論 1 296
  • 正文 為了忘掉前任粱挡,我火速辦了婚禮,結(jié)果婚禮上单起,老公的妹妹穿的比我還像新娘抱怔。我一直安慰自己,他們只是感情好嘀倒,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評(píng)論 6 397
  • 文/花漫 我一把揭開白布屈留。 她就那樣靜靜地躺著,像睡著了一般测蘑。 火紅的嫁衣襯著肌膚如雪灌危。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,246評(píng)論 1 308
  • 那天碳胳,我揣著相機(jī)與錄音勇蝙,去河邊找鬼。 笑死挨约,一個(gè)胖子當(dāng)著我的面吹牛味混,可吹牛的內(nèi)容都是我干的产雹。 我是一名探鬼主播,決...
    沈念sama閱讀 40,819評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼翁锡,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼蔓挖!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起馆衔,我...
    開封第一講書人閱讀 39,725評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤瘟判,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后角溃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拷获,經(jīng)...
    沈念sama閱讀 46,268評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評(píng)論 3 340
  • 正文 我和宋清朗相戀三年减细,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了匆瓜。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,488評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡邪财,死狀恐怖陕壹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情树埠,我是刑警寧澤糠馆,帶...
    沈念sama閱讀 36,181評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站怎憋,受9級(jí)特大地震影響又碌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜绊袋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評(píng)論 3 333
  • 文/蒙蒙 一毕匀、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧癌别,春花似錦皂岔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至圾笨,卻和暖如春教馆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背擂达。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工土铺, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓悲敷,卻偏偏與公主長(zhǎng)得像究恤,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子后德,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359

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