こんにちは この記事は、 Core Data Framework
が最小限の開発者を対象としています。 Core Data
は、デバイスにデータを保存し、それと対話するためのフレームワークであることを思い出させてください。 この主題に関するロシア語の記事がハブとネットワーク上にたくさんあるので、コンテンツを繰り返す必要はありません。
多くの場合初心者 特にスタックオーバーフロー 開発者はCore Data Framework
を使用するのが怖いので怖いです。または、機能のごく一部しか使用していません。 実際には、このフレームワークのクラスの基本機能の知識により、開発者はモデルを簡単に操作できます。
この記事では、次の点に焦点を当てたいと思います。
- モデルからデータを取得するクエリを作成するために使用される
NSFetchRequest
クラスを見てNSFetchRequest
ます。 その基本的な特性とケースをアプリケーションで調べます。 - 例として
NSFetchRequest
を使用して、NSFetchRequest
を使用して抽出されたデータを効率的に提示する際に、NSFetchedResultsController
機能と操作を詳細にNSFetchedResultsController
NSFetchRequest
。
デモプロジェクトの説明
「実験を設定する」デモプロジェクトは非常に原始的です。 これには、 UITableView
を含むModel
とViewController
が含まれUITableView
。
具体的には、製品の名前と価格の一般的なリストを使用します。
モデル
モデルには、 name
属性とprice
属性を持つProducts
と、これらの属性を継承するFavoriteProducts
name
2つのエンティティが含まれます。
たとえば、ランダムな価格( 1000
)とリストからの製品名を持つ特定の数の製品をデータベースに入力します: ””, “”, “”, “”, “ «»”, “ «»”, “”, “”
。
コントローラー
コードを使用して、コントローラーでテーブルを初期化し、フルスクリーンに配置します。
- (UITableView *)tableView { if (_tableView != nil) return _tableView; _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped]; _tableView.backgroundColor = [UIColor whiteColor]; _tableView.dataSource = self; return _tableView; } - (void)loadView { [super loadView]; [self.view addSubview:self.tableView]; } - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; _tableView.frame = self.view.frame; }
var tableView: UITableView = { let tableView = UITableView(frame: CGRectZero, style: .Grouped) tableView.backgroundColor = UIColor.whiteColor() return tableView }() override func loadView() { super.loadView() self.view.addSubview(tableView) } override func viewDidLoad() { super.viewDidLoad() tableView.dataSource = self } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() tableView.frame = self.view.frame }
データ抽出
NSManagedObjectContext
executeFetchRequest(_:)
メソッドを使用して、モデルからデータが抽出されます。 このメソッドの引数は、クエリクエリNSFetchRequest
この記事の主人公です。
NSManagedObjectContext *context = [[CoreDataManager instance] managedObjectContext]; NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Products" inManagedObjectContext:context]; NSFetchRequest *request = [[NSFetchRequest alloc] init]; request.entity = entityDescription; NSError *error = nil; NSArray* objects = [context executeFetchRequest:request error:&error];
let context = CoreDataManager.instance.managedObjectContext let entityDescription = NSEntityDescription.entityForName("Products", inManagedObjectContext: context) let request = NSFetchRequest() request.entity = entityDescription do { let objects = try context.executeFetchRequest(request) } catch { fatalError("Failed to fetch employees: \(error)") }
executeFetchRequest(_:)
メソッドの戻り値の型は、 NSManagedObject
オブジェクトのexecuteFetchRequest(_:)
配列です。 明確にするために、モデルから抽出された要素を印刷して、出力を変換します。
NAME: , PRICE: 156 NAME: , PRICE: 425 NAME: , PRICE: 85 NAME: «», PRICE: 400 NAME: , PRICE: 920 NAME: «», PRICE: 861 NAME: , PRICE: 76 NAME: , PRICE: 633 NAME: , PRICE: 635 NAME: «», PRICE: 718 NAME: , PRICE: 701 NAME: , PRICE: 176 NAME: , PRICE: 731 NAME: «», PRICE: 746 NAME: , PRICE: 456 NAME: , PRICE: 519 NAME: «», PRICE: 221 NAME: , PRICE: 560 NAME: «», PRICE: 646 NAME: , PRICE: 492 NAME: , PRICE: 185 NAME: , PRICE: 539 NAME: «», PRICE: 872 NAME: , PRICE: 972 NAME: , PRICE: 821 NAME: , PRICE: 409 NAME: , PRICE: 334 NAME: , PRICE: 734 NAME: , PRICE: 448 NAME: «», PRICE: 345
NSFetchRequestクラスの基本的なメソッドとプロパティ
上で言ったように、 NSFetchRequest
クラスNSFetchRequest
、モデルからデータを取得するためのクエリとして使用されます。 このツールを使用すると、データベースからオブジェクトを抽出する段階で、オブジェクトのフィルタリングと並べ替えのルールを設定できます。 この操作は、最初にすべてのオブジェクトを抽出する場合(そして10,000以上ある場合)よりも何倍も効率的かつ生産的になります。そして、目的のデータを手動でソートまたはフィルター処理します。
このクラスの基本的なプロパティを知っていれば、追加のアルゴリズムや松葉杖を開発することなく、クエリで簡単に操作し、特定の選択を取得できます。すべてがCore Data
既に実装されています。 始めましょう。
sortDescriptors
@property (nonatomic, strong) NSArray <NSSortDescriptor *> *sortDescriptors
var sortDescriptors: [NSSortDescriptor]?
NSSortDescriptor
クラスのオブジェクトの配列であるsortDesctriptors
、クラスプロパティの概要を開始したいと思います。 ソートメカニズムが実装されるのは、それらの助けを借りてです。 ソート記述子の使用方法については、 Appleポータルを参照してください。 このプロパティは、並べ替え記述子の配列を受け入れます。これにより、いくつかの並べ替え規則を使用できます。 この使用の優先順位は、キューのルール(FIFO、先入れ先出し)と同等です。オブジェクトが配列内にあるインデックスが小さいほど、ソートの優先順位が高くなります。
使用例
便宜上、このリストを最初に製品名でソートし、ソートキーで名前属性name
を指定してから価格でソートします。 名前をアルファベット順に並べ替え、価格を昇順で並べ替えます。 これを行うには、両方の述語のascending
値をブール値true
に設定しtrue
(ブール値false
降順でソートするために使用されます)。
NSSortDescriptor *nameSortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES]; NSSortDescriptor *priceSortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"price" ascending:YES]; fetchRequest.sortDescriptors = @[nameSortDescriptor, priceSortDescriptor];
let nameSortDescriptor = NSSortDescriptor(key: "name", ascending: true) let priceSortDescriptor = NSSortDescriptor(key: "price", ascending: true) fetchRequest.sortDescriptors = [nameSortDescriptor, priceSortDescriptor]
NAME: , PRICE: 185 NAME: , PRICE: 334 NAME: , PRICE: 731 NAME: , PRICE: 972 NAME: , PRICE: 156 NAME: , PRICE: 492 NAME: , PRICE: 701 NAME: , PRICE: 821 NAME: , PRICE: 76 NAME: , PRICE: 85 NAME: , PRICE: 176 NAME: , PRICE: 425 NAME: , PRICE: 448 NAME: , PRICE: 539 NAME: , PRICE: 635 NAME: «», PRICE: 345 NAME: «», PRICE: 646 NAME: «», PRICE: 718 NAME: «», PRICE: 746 NAME: «», PRICE: 861 NAME: «», PRICE: 872 NAME: «», PRICE: 221 NAME: «», PRICE: 400 NAME: , PRICE: 409 NAME: , PRICE: 633 NAME: , PRICE: 734 NAME: , PRICE: 456 NAME: , PRICE: 519 NAME: , PRICE: 560 NAME: , PRICE: 920
述語
@property (nonatomic, strong) NSPredicate *predicate
var predicate: NSPredicate?
検討中の次のプロパティは、データをフィルタリングするための強力で高速なツールであるNSPredicate
クラスのpredicate
です。 述語の使用に関するAppleの優れたガイドがあります( Translation )。 データは、前述のガイドで説明されている述語の特別な文字列構文のため、要求に応じて除外されます。
使用例
簡単な例から始めましょう。私たちは乳製品ソーセージの情熱的な愛好家であり、製品リストに掲載されている価格を知りたいと思っています。 これを行うために、述語で、名前属性name
が行" «»"
。に等しいオブジェクトを取得することを示します。
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name == %@", @" «»"]; fetchRequest.predicate = predicate;
let predicate = NSPredicate(format: "name == %@", " «»") fetchRequest.predicate = predicate
NAME: «», PRICE: 400 NAME: «», PRICE: 221
等価演算子==
を使用して述語を正しく構成するには、大文字と小文字を区別して文字列値を指定する必要があることに注意してください。
乳製品だけでなく、あらゆる種類のソーセージの価格を確認したい場合はどうすればよいですか? これを行うには、 CONTAINS
演算子(左の式は
右)に
発音区別記号に対する大文字と小文字の区別を示すキーワード[cd]
を追加します。 AND
演算子が役立ついくつかの条件を使用することもできます。 値によって結果を制限-最大500通貨単位。
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name CONTAINS[cd] %@ AND price < %d", @"", 500]; fetchRequest.predicate = predicate;
let predicate = NSPredicate(format: "name CONTAINS[cd] %@ AND price < %d", "", 500) fetchRequest.predicate = predicate
NAME: «», PRICE: 400 NAME: «», PRICE: 221 NAME: «», PRICE: 345
fetchLimit
@property (nonatomic) NSUInteger fetchLimit
var fetchLimit: Int
fetchLimit
プロパティを使用fetchLimit
と、取得するオブジェクトの数を制限できfetchLimit
。
使用例
実証するために、製品リストから最も安い12個の製品を取得します。 これを行うには、価格と回復可能なオブジェクトの数の制限による並べ替えを追加します12
。
// NSSortDescriptor *priceSortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"price" ascending:YES]; fetchRequest.sortDescriptors = @[priceSortDescriptor]; // = 12 fetchRequest.fetchLimit = 12;
// let priceSortDescriptor = NSSortDescriptor(key: "price", ascending: true) fetchRequest.sortDescriptors = [priceSortDescriptor] // = 12 fetchRequest.fetchLimit = 12
NAME: , PRICE: 76 NAME: , PRICE: 85 NAME: , PRICE: 156 NAME: , PRICE: 176 NAME: , PRICE: 185 NAME: «», PRICE: 221 NAME: , PRICE: 334 NAME: «», PRICE: 345 NAME: «», PRICE: 400 NAME: , PRICE: 409 NAME: , PRICE: 425 NAME: , PRICE: 448
fetchOffset
@property (nonatomic) NSUInteger fetchOffset
var fetchOffset: Int
このプロパティを使用すると、指定したオブジェクト数だけ選択結果をシフトできます。
使用例
このプロパティの操作を示すために、前のクエリを使用して、2つのオブジェクトによるオフセットを追加します。 結果として、最初の2つが欠落している12個のオブジェクトを取得し、結果を含むテーブルを2つのセルにシフトしたかのように、次のものが最後に追加されます。
fetchRequest.fetchOffset = 2;
fetchRequest.fetchOffset = 2
明確にするために、抽出したオブジェクトを点線に制限しました。
: : NAME: , PRICE: 76 NAME: , PRICE: 85 --------------------------------------------------------------------------------- NAME: , PRICE: 76 NAME: , PRICE: 156 NAME: , PRICE: 85 NAME: , PRICE: 176 NAME: , PRICE: 156 NAME: , PRICE: 185 NAME: , PRICE: 176 NAME: «», PRICE: 221 NAME: , PRICE: 185 NAME: , PRICE: 334 NAME: «», PRICE: 221 NAME: «», PRICE: 345 NAME: , PRICE: 334 NAME: «», PRICE: 400 NAME: «», PRICE: 345 NAME: , PRICE: 409 NAME: «», PRICE: 400 NAME: , PRICE: 425 NAME: , PRICE: 409 NAME: , PRICE: 448 NAME: , PRICE: 425 NAME: , PRICE: 456 NAME: , PRICE: 448 NAME: , PRICE: 492 --------------------------------------------------------------------------------- NAME: , PRICE: 456 ... NAME: , PRICE: 492 ...
fetchBatchSize
@property (nonatomic) NSUInteger fetchBatchSize
var fetchBatchSize: Int
fetchBatchSize
を使用すると、 Core Data Framework
動作するデータベース( Persistent Store
)( SQLite
、 XML
など)から取得するオブジェクトの数を制御fetchBatchSize
ます。 特定の場合に値を正しく設定すると、データベースの処理速度が向上し、逆に速度が低下します。
UITableView
作業しているとしましょう。 このモデルでは、10,000以上のオブジェクト。 これらのすべての要素を一度に抽出するには時間がかかります。 ただし、画面には20個のセルがあり、表示には20個のオブジェクトしか必要ありません。 このような場合、 fetchBatchSize
を20に設定することをお勧めします。最初に、 Core Data
はデータベースにテーブルに表示する20個のオブジェクトを要求し、スクロールすると次の20個の要素のバンドルが要求されます。 このアプローチにより、永続ストレージとの対話が大幅に最適化されます。
ただし、1などの小さすぎるパケットサイズは使用しないでください。これにより、1つの要素に対する一定のクエリでデータベースがロードされます。
fetchRequest.fetchBatchSize = 20;
fetchRequest.fetchBatchZize = 20
resultType
@property (nonatomic) NSFetchRequestResultType resultType
var resultType: NSFetchRequestResultType
データを取得するとき、 executeFetchRequest(_:)
メソッドはデフォルトでNSManagedObject
クラスとその子孫のオブジェクトの配列を返します。
resultType
プロパティを変更すると、抽出されたオブジェクトのタイプを選択できresultType
。 それらを考慮してください(NSプレフィックス付きのObjective-Cは、スラッシュを介してSwiftと交互になります):
-
NSManagedObjectResultType
/ManagedObjectResultType
-NSManagedObject
クラスとその子孫のオブジェクト(デフォルト)。 -
NSManagedObjectIDResultType
/ManagedObjectIDResultType
-NSManagedObjectIDResultType
の識別子。 -
NSDictionaryResultType
/DictionaryResultType
はディクショナリです。キーはエンティティの属性の名前です。 -
NSCountResultType
/CountResultType
要素数の値を持つ配列の1つの要素を返します。
propertiesToFetch
@property (nonatomic, copy) NSArray *propertiesToFetch
var propertiesToFetch: [AnyObject]?
このプロパティを使用すると、エンティティから必要な属性のみを抽出できます。 ただし、必要条件は、 resultType
が辞書( NSDictionaryResultType
/ DictionaryResultType
)でなければならないことです。
使用例
例として、 name
属性の値のみを抽出し、出力のために、すべての既存の辞書キーのすべての値を(key: value)
形式で出力します。
fetchRequest.resultType = NSDictionaryResultType; fetchRequest.propertiesToFetch = @[@"name"];
fetchRequest.resultType = .DictionaryResultType fetchRequest.propertiesToFetch = ["name"]
name: name: name: name: «» name: name: «» name: name: name: name: «» name: name: name: name: «» name: name: name: name: name: «» name: name: name: name: «» name: name: name: name: name: name: name: «»
含む
@property (nonatomic) BOOL includesSubentities
var includesSubentities: Bool
このプロパティをデモンストレーションするには、 Products
の後継であるFavoriteProducts
エンティティにオブジェクトを追加する必要があります。 このオブジェクトに名前""
と999
の価格を付けます。
Products
エンティティのクエリに移り、名前と価格でソートします。
NAME: , PRICE: 185 NAME: , PRICE: 334 NAME: , PRICE: 731 NAME: , PRICE: 972 NAME: , PRICE: 156 NAME: , PRICE: 492 NAME: , PRICE: 701 NAME: , PRICE: 821 NAME: , PRICE: 76 NAME: , PRICE: 85 NAME: , PRICE: 176 NAME: , PRICE: 425 NAME: , PRICE: 448 NAME: , PRICE: 539 NAME: , PRICE: 635 NAME: «», PRICE: 345 NAME: «», PRICE: 646 NAME: «», PRICE: 718 NAME: «», PRICE: 746 NAME: «», PRICE: 861 NAME: «», PRICE: 872 NAME: «», PRICE: 221 NAME: «», PRICE: 400 NAME: , PRICE: 409 NAME: , PRICE: 633 NAME: , PRICE: 734 NAME: , PRICE: 456 NAME: , PRICE: 519 NAME: , PRICE: 560 NAME: , PRICE: 920 NAME: , PRICE: 999
リストの最後で、 FavoriteProducts
エンティティに追加したオブジェクトに注目してください。 彼はここで何をしていますか? 実際には、 includesSubentities
プロパティの要求値はデフォルトでブール値true
等しいため、現在のエンティティだけでなく、子孫エンティティのオブジェクトも取得します。
これを回避するには、ブール値false
変更しfalse
。
fetchRequest.includesSubentities = NO;
fetchRequest.includesSubentities = false
NAME: , PRICE: 185 NAME: , PRICE: 334 NAME: , PRICE: 731 NAME: , PRICE: 972 NAME: , PRICE: 156 NAME: , PRICE: 492 NAME: , PRICE: 701 NAME: , PRICE: 821 NAME: , PRICE: 76 NAME: , PRICE: 85 NAME: , PRICE: 176 NAME: , PRICE: 425 NAME: , PRICE: 448 NAME: , PRICE: 539 NAME: , PRICE: 635 NAME: «», PRICE: 345 NAME: «», PRICE: 646 NAME: «», PRICE: 718 NAME: «», PRICE: 746 NAME: «», PRICE: 861 NAME: «», PRICE: 872 NAME: «», PRICE: 221 NAME: «», PRICE: 400 NAME: , PRICE: 409 NAME: , PRICE: 633 NAME: , PRICE: 734 NAME: , PRICE: 456 NAME: , PRICE: 519 NAME: , PRICE: 560 NAME: , PRICE: 920
フェッチ結果コントローラー(FRC)
NSFetchedResultsController
クラスのコントローラーは、 Core Data
とViewController
間に任意に配置できます。ここで、データベースのデータを表示する必要があります。 このコントローラーのメソッドとプロパティを使用すると、 Core Data
オブジェクトを、最適化されたUITableView
テーブルと組み合わせて、簡単に対話、表現、管理できます。
このコントローラーは、抽出されたオブジェクトをテーブル要素(これらのセクションのセクションおよびオブジェクト)に変換できます。 FRC
にはNSFetchedResultsControllerDelegate
プロトコルがあり、委任されると、コントローラーの初期化時に特定のNSFetchRequest
要求のオブジェクトで発生する変更をキャッチできます。
FRCを初期化する
- (instancetype)initWithFetchRequest:(NSFetchRequest *)fetchRequest managedObjectContext: (NSManagedObjectContext *)context sectionNameKeyPath:(nullable NSString *)sectionNameKeyPath cacheName:(nullable NSString *)name;
public init(fetchRequest: NSFetchRequest, managedObjectContext context: NSManagedObjectContext, sectionNameKeyPath: String?, cacheName name: String?)
初期化パラメーターを分析しましょう。
-
fetchRequest
-NSFetchRequest
オブジェクトを取得するリクエスト。 重要:FRC
が機能するには、リクエストに少なくとも1つのソート記述子が必要であり、そのresultType
がNSManagedObjectResultType / ManagedObjectResultType
必要があります。 -
NSManagedObjectContext
しているNSManagedObjectContext
のコンテキスト。 -
sectionNameKeyPath
オプションのパラメーター。文字列キー(エンティティー属性名)の形式で指定された場合、オブジェクトはテーブルセクションのこの属性の同じ値でグループ化されます。 このキーは、優先度が最も高いソート記述子と一致することが重要です。 このパラメーターが指定されていない場合、1つのセクションを持つテーブルが作成されます。 -
cacheName
オプションのパラメーター。指定すると、コントローラーはクエリ結果のキャッシュを開始します。 後で詳しく検討します。
次のステップは、データベースから選択を取得するためにperformFetch
コントローラーperformFetch
を呼び出すことperformFetch
。
NSError *error = nil; if (![self.fetchedResultsController performFetch:&error]) { NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); }
do { try fetchedResultsController.performFetch() } catch { print(error) }
メソッドはブール値を返します。 抽出が成功した場合、ブール値はtrue
返し、そうでない場合はfalse
返しtrue
。 抽出後、オブジェクトはfetchedObjects
コントローラーfetchedObjects
。
UITableViewとの相互作用
テーブルの使用を検討してください。 抽出されたオブジェクトはfetchedObject
プロパティにありますが、それらを操作するには、 sections
コントローラープロパティを参照する必要があります。 これは、次のプロパティを説明するNSFetchedResultsSectionInfo
プロトコルにサブスクライブされるオブジェクトの配列です。
-
name
セクション名。 -
indexTitle
セクションのタイトル。 -
numbersOfObjects
セクション内のオブジェクトの数。 -
objects
セクション自体のオブジェクトの配列。
実装
便宜上、 configureCell
テーブルのセルを構成するメソッドを追加します。
#pragma mark - Table View - (void)configureCell:(UITableViewCell *)cell withObject:(Products *)object { cell.textLabel.text = object.name; cell.detailTextLabel.text = [NSString stringWithFormat:@"%d", object.price.intValue]; } #pragma mark UITableViewDataSource - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return [[self.fetchedResultsController sections] count]; } - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section]; return sectionInfo.indexTitle; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section]; return [sectionInfo numberOfObjects]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *identifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:identifier]; } Products *object = [[self fetchedResultsController] objectAtIndexPath:indexPath]; [self configureCell:cell withObject:(Products *)object]; return cell; }
// MARK: - Table View extension ViewController { func configureCell(cell: UITableViewCell, withObject product: Products) { cell.textLabel?.text = product.name ?? "" cell.detailTextLabel?.text = String(product.price ?? 0) } } // MARK: UITableViewDataSource extension ViewController: UITableViewDataSource { func numberOfSectionsInTableView(tableView: UITableView) -> Int { guard let sections = fetchedResultsController.sections else { return 0 } return sections.count } func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? { guard let sections = fetchedResultsController.sections else { return nil } return sections[section].indexTitle ?? "" } func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { guard let sections = fetchedResultsController.sections else { return 0 } return sections[section].numberOfObjects } func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let identifier = "Cell" let product = fetchedResultsController.objectAtIndexPath(indexPath) as! Products var cell = tableView.dequeueReusableCellWithIdentifier(identifier) if cell == nil { cell = UITableViewCell(style: .Value1, reuseIdentifier: identifier) } configureCell(cell!, withObject: product) return cell! } }
NSFetchRequest
sectionNameKeyPath
FRC
"name"
, .
FRC
FRC :
-
(delegate = nil, cacheName = nil)
— . - ,
(delegate != nil, cacheName = nil)
—NSFetchedResultsControllerDelegate
, . . - ,
(delegate != nil, cacheName = <#NSString/String#>)
— .
NSFetchedResultsControllerDelegate
NSFetchedResultsControllerDelegate
, , NSFetchRequest
. UITableView
, UI- , .
#pragma mark - NSFetchedResultsControllerDelegate // 1 - (NSString *)controller:(NSFetchedResultsController *)controller sectionIndexTitleForSectionName:(NSString *)sectionName { return sectionName; } // 2 - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { [self.tableView beginUpdates]; } // 3 - (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; default: return; } } // 4 - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { UITableView *tableView = self.tableView; switch(type) { case NSFetchedResultsChangeInsert: [tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeDelete: [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeUpdate: [self configureCell:[tableView cellForRowAtIndexPath:indexPath] withObject:anObject]; break; case NSFetchedResultsChangeMove: [tableView moveRowAtIndexPath:indexPath toIndexPath:newIndexPath]; break; } } // 5 - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { [self.tableView endUpdates]; }
// MARK: - NSFetchedResultsControllerDelegate extension ViewController: NSFetchedResultsControllerDelegate { // 1 func controller(controller: NSFetchedResultsController, sectionIndexTitleForSectionName sectionName: String) -> String? { return sectionName } // 2 func controllerWillChangeContent(controller: NSFetchedResultsController) { tableView.beginUpdates() } // 3 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) default: return } } // 4 func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) { switch type { case .Insert: if let indexPath = newIndexPath { tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic) } case .Update: if let indexPath = indexPath { let product = fetchedResultsController.objectAtIndexPath(indexPath) as! Products guard let cell = tableView.cellForRowAtIndexPath(indexPath) else { break } configureCell(cell, withObject: product) } case .Move: if let indexPath = indexPath { tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic) } if let newIndexPath = newIndexPath { tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Automatic) } case .Delete: if let indexPath = indexPath { tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic) } } } // 5 func controllerDidChangeContent(controller: NSFetchedResultsController) { tableView.endUpdates() } }
sectionIndexTitleForSectionName
— , . , , (sectionName
) . . .
controllerWillChangeContent
— , .UITableView
—beginUpdates
.
didChangeSection
— , , . :sectionInfo
— , ,sectionIndex
—type
NSFetchedResultsChangeType
, (Insert, Delete, Move, Update). .
didChangeObject
— , ,sectionInfo
,anObject
, ,sectionIndex
— ,indexPath
newIndexPath
, .UITableView
, , , , .
-
controllerDidChangeContent
— .endUpdates
.
— ""
""
1
.
The Cache
. . . (deleteCacheWithName:)
, . Core Data , cacheName
.
?
-
cacheName
, . - , ( , , ). — , , .
まとめ
NSFetchRequest
, Core Data
, . Fetched Results Controller
, , UITableView
.
, , .