Help to deal with CoreData - you need to get the number of positions in the sqlite storage file. Implemented such a method - returns zeros.

 -(NSInteger) getStorageElementCount { NSFetchRequest *request = [[NSFetchRequest alloc] init]; NSEntityDescription *description = [NSEntityDescription entityForName:@"TMEmployee" inManagedObjectContext:self.managedObjectContext]; [request setEntity:description]; NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES]; [request setSortDescriptors:@[sortDescriptor]]; self.fetchController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil]; self.fetchController.delegate = self; return [[self.fetchController fetchedObjects] count]; } 

Before that I did without NSFetchedResultsController - on the contrary, I returned the amount that I understand in context and not in the repository

 return [[self.managedObjectContext executeFetchRequest:request error:nil] count]; 
  • I understand that after you have created fetchRequest , you still have to performFetch: call it up to get results (purely from the documentation, did not try it yourself) - Max Mikheyenko

1 answer 1

Practical part

As Max Mikheyenko commented quite correctly, the FRC receives the objects after -performFetch .

However, in order to obtain the number of objects once, the use of FRC is superfluous. It is enough to do, for example, like this:

 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"TMEmployee"]; request.predicate = [NSPredicate predicateWithFormat:@"kind == %@", @(TMEmployeeKind)]; NSError *error; NSUInteger count = [context countForFetchRequest:request error:&error]; 

The predicate does not need to be specified if you need to get the number of all objects of the TMEmployee entity.

I also want to note that sortDescriptors are not needed here for obvious reasons.


Theoretical part

Outline the core ideology of Core Data .

What is it all about?

The task performed by Core Data is the application data management. Here the library gives us the opportunity to balance between several conflicting requirements:

  • Quick access to data.
  • Convenience of work.
  • Low memory profile.

The unofficial definition of Core Data is an object graph management system with dancers and chess. That is, the system holds objects and connections between them, and provides pleasant utilities such as lazy loading of data and automatic notification of changes in objects. Full list of features here .

Why it is worth using

In short, because if we tried to base our data model on pure SQLite , for example, with further scaling of the application and increasing the amount of data, we will eventually find out that we are writing our Core Data . And it already solved, for example:

  • Faulting and Uniquing . By default, when the query is executed, the object is loaded into the context as a lightweight "stub". When accessing any of its properties, Core Data fills this object with data. Such a stub is called a fault , and the process of loading data into it is faulting . Actually, a fault is simply a possible state of an object of the NSManagedObject class. He, by the way, is unique. Each request to the context, the results of which contain some specific object, will return a pointer to the same instance of the object - thus avoiding duplication of data in memory.
  • Batching The query results can be loaded in batches, for 50 pieces, for example. It is very convenient for large data sets, about which it is known that they will be displayed sequentially, as, for example, when scrolling a table. When you have to show the 51st line, and the UITableView delegate will -tableView:cellForRowAtIndexPath: method, the delegate will execute DBPerson *person = [self.frc objectAtIndexPath:indexPath]; to retrieve data from the 51st object DBPerson *person = [self.frc objectAtIndexPath:indexPath]; , and Core Data itself will request the next package. Enabled by one line request.fetchBatchSize = 50; .
  • Convenient access to properties and connections of objects. After generating the entity classes (Editor / Create NSManagedObject subclass ...), the properties of Core Data objects can be accessed in the usual way:

     DBDepartment *department = [self.frc objectAtIndexPath:indexPath]; DBPerson *person = [NSEntityDescription insertNewObjectForEntityForName:@"DBPerson" inManagedObjectContext:context]; person.nameLast = @"Добчинский"; person.nameFirst = @"ΠŸΡ‘Ρ‚Ρ€"; person.nameMiddle = @"Π˜Π²Π°Π½ΠΎΠ²ΠΈΡ‡"; person.department = department; 

The library has a rather long history, rooted in EOF NeXT, and has been very well tested in its more than ten-year history. That does not exclude, of course, the presence of almost as ancient glitches in it .

What is a "stack"

This is a collection of objects that control the data layer in Core Data . For example, like this:

 [NSPersistentStore] β†ž [NSPersistentStoreCoordinator] β†  [NSManagedObjectContext] ↓ ↑ [SQLite file] [NSManagedObjectModel] 

The best-known example of a stack can be viewed by creating a new project in Xcode with the "Use Core Data" AppDelegate on, it will be in the AppDelegate class.

Context

The application interacts with the data through NSManagedObjectContext . In the most common use case, mainContext is the current state of the application data, what the interface is attached to, what the user sees. This data can be changed by adding or deleting objects, changing their properties and establishing or breaking links between them in accordance with user actions, or, for example, when receiving data from a web service. All these changes are in the context memory and can be synchronously displayed in the interface. If necessary, these changes can be saved so that the current state of the data survives the termination of application execution or device shutdown. The content of the context is not necessarily equivalent to the complete data graph of the application. Only those objects that are directly or indirectly requested using the FRC , -executeFetchRequest:error: -objectWithID: etc. are -objectWithID: . NSManagedObjectContext objects are very cheap to create, they should not be afraid to create and release for any intermediate calculations.

The data source for the context can be either the NSPersistentStoreCoordinator , or the parent context.

Model

NSManagedObjectModel describes the data structure of an application: the types of objects, their properties, and their relationships. It may also contain some other information - configurations and model versions. Usually, a model is created visually, in the Xcodov Core Data Model Editor, but it can also be programmed.

Storage

NSPersistentStore is responsible for the physical storage of data. Established there are four types: SQLite , Binary , XML and In-Memory . Most of them will use SQLite . Technically, you can create your own storage class that receives data from a web service, connect this storage to the Core Data stack, and work with it as you would a regular SQLite file. And such attempts were.

Coordinator

NSPersistentStoreCoordinator binds the repository (or several repositories) to the data model and issues and stores data on the requests of the associated contexts NSManagedObjectContext , organizing these requests into a queue.

In iOS 10, the coordinator supports parallel execution of several reading tasks plus one writing task. In systems up to the ninth, it sometimes makes sense to keep several coordinators connected to the same SQLite file (see the section on large imports below).

Example

I give a simple code for initializing the stack in the form of a singleton and the connection of the main and background contexts:

DataSource.h

 @interface DataSource : NSObject @property (nonatomic, readonly) NSPersistentStoreCoordinator *coordinator; @property (nonatomic, readonly) NSManagedObjectContext *mainContext; @property (nonatomic, readonly) NSManagedObjectContext *backgroundContext; + (instancetype)shared; @end 

DataSource.m

 @interface DataSource () @property (nonatomic, strong) NSPersistentStoreCoordinator *coordinator; @property (nonatomic, strong) NSManagedObjectContext *mainContext; @property (nonatomic, strong) NSManagedObjectContext *backgroundContext; @end @implementation DataSource + (instancetype)shared { static dispatch_once_t onceToken; static id _singleton; dispatch_once(&onceToken, ^{ _singleton = [[self alloc] initInternal]; }); return _singleton; } - (instancetype)init { NSLog(@"Никаких alloc] init]! Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉΡ‚Π΅ singleton [DataSource shared]."); abort(); return nil; } - (instancetype)initInternal { self = [super init]; if (!self) { return nil; } // стэк Core Data NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Model" withExtension:@"momd"]; NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]; self.coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; NSURL *documentsURL = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; NSURL *storeURL = [documentsURL URLByAppendingPathComponent:@"Data.sqlite"]; NSError *error = nil; NSDictionary *options = @{ NSMigratePersistentStoresAutomaticallyOption: @YES, NSInferMappingModelAutomaticallyOption: @YES, }; NSPersistentStore *store = [self.coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]; if (!store) { error = [NSError errorWithDomain:@"MY-OWN-ERROR-DOMAIN" code:9999 userInfo:@{ NSLocalizedDescriptionKey: @"НС ΡƒΠ΄Π°Π»ΠΎΡΡŒ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ Ρ…Ρ€Π°Π½ΠΈΠ»ΠΈΡ‰Π΅ Core Data", NSUnderlyingErrorKey: error, }]; NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } self.mainContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; self.mainContext.persistentStoreCoordinator = self.coordinator; self.backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; self.backgroundContext.persistentStoreCoordinator = self.coordinator; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mainContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:self.mainContext]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(backgroundContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:self.backgroundContext]; return self; } - (void)mainContextDidSave:(NSNotification *)notification { [self.backgroundContext performBlock:^{ [self.backgroundContext mergeChangesFromContextDidSaveNotification:notification]; }]; } - (void)backgroundContextDidSave:(NSNotification *)notification { [self.mainContext performBlock:^{ [self.mainContext mergeChangesFromContextDidSaveNotification:notification]; }]; } @end 

How to get data

After the stack is assembled, you can query it for existing data, create new objects and save changes.

NSFetchRequest is a context call describing the type of objects requested, filtering criteria, sorting order, and still quite a significant amount of much less widely known parameters.

At the NSPersistentStore SQLite level, for example, of a type, the query is translated into SQL commands and passed to the SQLite library.

You can use the query in several ways. From common:

  1. Ask the context to execute this query with the -executeFetchRequest:error: method -executeFetchRequest:error: The result will be an array. In the simplest case, with the default resultType == NSManagedObjectResultType , this array will contain NSManagedObject objects.
  2. Ask the context to count the number of objects for this request using the -countForFetchRequest:error: method. It is executed significantly faster due to the fact that property values ​​are not loaded, and NSManagedObject objects are not created.
  3. Send this request to NSFetchedResultsController . After performing performFetch: FRC will receive objects matching this request. If you also specify a delegate for the FRC, it will continuously monitor changes in the NSManagedObjectContext context, and notify the delegate via the NSFetchedResultsControllerDelegate protocol about the changes in the state of the objects.

Items 1 and 2 are useful for some one-time actions.

Point 3 is used to permanently link the state of the data with the interface, UITableView , for example.

How to count the units

For example:

 - (void)calculateAggregateValues { NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; context.persistentStoreCoordinator = [[DataSource shared] coordinator]; [context performBlock:^{ // Π½Π°ΠΈΠΌΠ΅Π½ΠΎΠ²Π°Π½ΠΈΠ΅ поставщика NSExpressionDescription *nameDescription = [[NSExpressionDescription alloc] init]; nameDescription.expression = [NSExpression expressionForKeyPath:@"supplierName"]; nameDescription.name = @"name"; nameDescription.expressionResultType = NSStringAttributeType; // количСство ΠΊΠΎΠ½Ρ‚Ρ€Π°ΠΊΡ‚ΠΎΠ² NSExpressionDescription *countDescription = [[NSExpressionDescription alloc] init]; countDescription.expression = [NSExpression expressionForKeyPath:@"sumPlanned.@count"]; countDescription.name = @"totalCount"; countDescription.expressionResultType = NSInteger32AttributeType; // планируСмая сумма NSExpressionDescription *sumDescription = [[NSExpressionDescription alloc] init]; sumDescription.expression = [NSExpression expressionForKeyPath:@"@sum.sumPlanned"]; sumDescription.name = @"totalSum"; sumDescription.expressionResultType = NSDoubleAttributeType; // запрос NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"DBContract"]; request.resultType = NSDictionaryResultType; request.predicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[ [NSPredicate predicateWithFormat:@"datePlanned < %@", [NSDate date]], ]]; request.propertiesToFetch = @[nameDescription, countDescription, sumDescription]; request.propertiesToGroupBy = @[nameDescription]; // Π²Ρ‹ΠΏΠΎΠ»Π½ΠΈΠΌ запрос NSArray *result = [context executeFetchRequest:request error:nil]; // Π²ΠΎΠ·ΡŒΠΌΡ‘ΠΌ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ с самой ΠΊΡ€ΡƒΠΏΠ½ΠΎΠΉ суммой NSDictionary *values = [[result sortedArrayUsingDescriptors:@[ [NSSortDescriptor sortDescriptorWithKey:@"totalSum" ascending:NO], ]] firstObject]; // ΠΏΠΎΠΊΠ°ΠΆΠ΅ΠΌ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Ρ‹ Π² интСрфСйсС dispatch_async(dispatch_get_main_queue(), ^{ self.nameLabel.text = values[@"name"]; self.countLabel.text = [values[@"totalCount"] stringValue]; self.sumLabel.text = [values[@"totalSum"] stringValue]; }); }]; } 

Bulky, but it works smartly. You can read, for example, here or in the documentation .

Why does the application need multiple contexts?

To unload the main thread. As soon as a difficult task appears, and the interface begins to slow down - the task must be led off to the background. For the case of counting aggregates, for example, an NSManagedObjectContext type NSPrivateQueueConcurrencyType , in its -performBlock: the task is executed, and the results are transferred to the interface in main thread via dispatch_async (see example above).

The case of large data import will be discussed below.

How to respond to changes in data

The task of the application is usually to synchronize the interface with the state of the data with as little delay as possible. NSFetchedResultsController specially created for this task: it tracks changes in a given set of context data and notifies its delegate ( UIViewController , for example) about them, which, upon receiving these notifications, updates the interface accordingly. The classic NSFetchedResultsController with UITableView is here .

The alternative to FRC is to subscribe to the NSManagedObjectContextDidChangeNotification notification, and independently parse the resulting NSNotification object.

How not to let the interface slow down

Very simple: leave in it only the data that the user sees, and not load what the user may never see.

For an interface, one common NSManagedObjectContext with the NSMainQueueConcurrencyType type is most often held β€” specifically designed to work in main thread. All NSManagedObject , the data from which will go to all sorts of UIlabel , UICollectionView , UITableView , etc. objects of interface classes must be generated by such a context, otherwise you will have to mess around with the fact that you can access the properties of NSManagedObject objects only in the stream of their context, and interface elements only in main thread.

Do not load unnecessary data - it is solved by faulting, batching and estimatedRowHeight methods for the case with different table heights. For example, in the data table - two thousand records. It does not make sense to load them all at once, as the user will see thirty lines at most on the first display of the table. The data of the remaining one thousand nine hundred and seventy objects will be loaded in vain, as the user immediately climbs into the search, and the data set is re-requested from Core Data with a significant reduction by the predicate, which takes into account the user input string. If UITableView not provided with the expected row height, then after receiving the number of sections and objects in sections from the delegate, the table will need to determine the overall height of its content, and it will rush to calculate (in the case of auto layout in rows) or require a delegate (in the case of -tableView:heightForRowAtIndexPath: heights of each of two thousand lines. In both cases, this will result in a line height calculation for the specified widths and attributes using the -boundingRectWithSize:options:attributes:context: method -boundingRectWithSize:options:attributes:context: - and this is an extremely expensive method that runs on main thread. On current devices, the interface will hang for about five seconds, or even more, depending on the complexity of the cells.

How to make custom changes

Typically, the user generates a surprisingly small change in the data. Of course, situations like "delete all records obtained from TSB" are possible. But more often, user activity is limited by insanely long-term text bobbing (this changes the single text property of the DBMessage object), tapes on the like button (one DBPerson object is added to the DBMessage connection of the DBMessage object) and DBPerson button (a modified object saved, in the background is serialized and passed to the web service API).

Such a trifle can and should be done directly in mainContext .

Creating and editing objects is conveniently done on a child context connected to mainContext and also of type NSMainQueueConcurrencyType . For example, an employee's account card opens a UIViewController UIViewController in the popover that creates a child context, and in it loads the specified or creates a new DBPerson object, the data of which is scattered over the controller's views for viewing and editing. The user makes his changes, saves "Save", the child context is sent -save: changes are pushed into mainContext , which can also be immediately saved so that the data in the NSManagedObjectContext -> NSPersistentStoreCoordinator -> NSPersistentStore would fall into the SQLite file.

If the user drops "Cancel" - you can simply remove the controller without saving the context. In this case, the changes will simply disappear with him.

How to make large imports

Slight data changes can be made directly to mainContext objects. But it makes sense to use background contexts ( NSPrivateQueueConcurrencyType ) to make large-scale changes to the data.

The following scheme proved to be viable:

  • The data filling procedure is carried out in the background thread. It is convenient to use NSOperationQueue - it supports a convenient mechanism for canceling the execution of its NSOperation tasks.
  • For iOS up to version 9, you should use your NSPersistentStoreCoordinator bound to the same SQLite file as the mainContext coordinator. It is necessary in order to avoid blocking at the level of the coordinator - this is a slow thing. If the user starts spinning the table at the moment when the background context writes its changes, the interface will stand, waiting until the overall coordinator has completed the task of the background context. Two coordinators on a single SQLite file reduce the total blocking time to the blocking time of the SQLite library, which is significantly faster. Here it is understood in detail, from 25:30.
  • Analysis of the source data should be done in batches, accumulating data for several thousand objects so that the system does not kill the application for greed for memory consumption. It's good to use something like NSXMLParser in SAX mode and wrap the bodies of all import methods in @autoreleasepool .
  • Upon reaching the packet limit, create a working context connected to your coordinator. In the stream of this context in the -performBlockAndWait: method -performBlockAndWait: create the necessary NSManagedObject or nafetchit existing, fill them with the received data, save the context and release it and the package data.
  • Changes made by the working context must be passed to mainContext . For this:

    • Before saving changes to the working context, we subscribe to the notification of its saving:

       - (void)processData { // создадим Ρ€Π°Π±ΠΎΡ‡ΠΈΠΉ контСкст NSManagedObjectContext *workerContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; workerContext.persistentStoreCoordinator = self.coordinator; [workerContext performBlockAndWait:^{ // ΠΎΠ±Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Π΅ΠΌ Π½Π°ΠΊΠΎΠΏΠ»Π΅Π½Π½Ρ‹Π΅ Π΄Π°Π½Π½Ρ‹Π΅ // подписываСмся Π½Π° ΡƒΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠ΅ ΠΎ сохранСнии [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contextDidSave:) name:NSManagedObjectContextDidSaveNotification object:workerContext]; // сохраняСм контСкст NSError *error; if (![workerContext save:&error] { // ΠΎΠ±Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Π΅ΠΌ ΠΎΡˆΠΈΠ±ΠΊΡƒ } // отписываСмся ΠΎΡ‚ ΡƒΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠΉ [[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:workerContext]; }]; } 
    • ΠŸΡ€ΠΈ сохранСнии Ρ€Π°Π±ΠΎΡ‡Π΅Π³ΠΎ контСкста ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ ΡƒΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠ΅, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ Ρ‚Π°ΠΊ:

       - (void)contextDidSave:(NSNotification *)notification { // сообщим ΠΎ Π½Π°Ρ‡Π°Π»Π΅ примСнСния ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ [[NSNotificationCenter defaultCenter] postNotificationName:DBWillUpdateDataNotification object:self]; // mainContext NSManagedObjectContext *mainContext = [[DataSource shared] mainContext]; [mainContext performBlockAndWait:^{ [mainContext mergeChangesFromContextDidSaveNotification:notification]; }]; // сообщим ΠΎΠ± ΠΎΠΊΠΎΠ½Ρ‡Π°Π½ΠΈΠΈ примСнСния ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ [[NSNotificationCenter defaultCenter] postNotificationName:DBDidUpdateDataNotification object:self]; } 
    • Π’ своих UIViewController Π°Ρ… слСдим Π·Π° увСдомлСниями DBWillUpdateDataNotification ΠΈ DBDidUpdateDataNotification . ΠŸΡ€ΠΈ Π½Π°Ρ‡Π°Π»Π΅ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠΉ отцСпляСм свои FRC ΠΎΡ‚ Π΄Π΅Π»Π΅Π³Π°Ρ‚Π° self.frc.delegate = nil; (FRC Π½Π΅ слСдит Π·Π° измСнСниями Π² контСкстС Π±Π΅Π· ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½Π½ΠΎΠ³ΠΎ Π΄Π΅Π»Π΅Π³Π°Ρ‚Π°). ΠŸΡ€ΠΈ ΠΎΠΊΠΎΠ½Ρ‡Π°Π½ΠΈΠΈ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠΉ Π·Π°Π½ΠΎΠ²ΠΎ цСпляСм Π΄Π΅Π»Π΅Π³Π°Ρ‚Π° ΠΊ FRC, Ρ€Π΅Ρ„Π΅Ρ‚Ρ‡ΠΈΠΌ FRC ΠΈ статичныС запросы, Π° ΠΏΠΎΡ‚ΠΎΠΌ обновляСм интСрфСйс – ΠΏΠ΅Ρ€Π΅Π·Π°Π³Ρ€ΡƒΠΆΠ°Π΅ΠΌ Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹, заполняСм тСкстом ΠΌΠ΅Ρ‚ΠΊΠΈ ΠΈ Ρ‚.ΠΏ.

    Π’Π°ΠΊΠΈΠ΅ пляски Π·Π΄ΠΎΡ€ΠΎΠ²ΠΎ Ρ€Π΅ΡˆΠ°ΡŽΡ‚ Π΄Π²Π΅ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹:

    1. ΠŸΡ€ΠΈ подтягивании ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠΌ -mergeChangesFromContextDidSaveNotification: ΠΎΠ±Π½ΠΎΠ²Π»ΡΡŽΡ‚ΡΡ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Ρ‚Π΅ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΡƒΠΆΠ΅ Π΅ΡΡ‚ΡŒ Π² обновляСмом контСкстС. Π‘ΠΎΠ·Π΄Π°Π½Π½Ρ‹Π΅ Π² Ρ€Π°Π±ΠΎΡ‡Π΅ΠΌ контСкстС ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹ Π½Π΅ ΠΏΠΎΠΏΠ°Π΄ΡƒΡ‚ Π² mainContext , ΠΎ Π½ΠΈΡ… Π½ΠΈΡ‡Π΅Π³ΠΎ Π½Π΅ ΡƒΠ·Π½Π°ΡŽΡ‚ FRC, ΠΎΠ½ΠΈ Π½Π΅ Π±ΡƒΠ΄ΡƒΡ‚ ΠΏΠΎΠΊΠ°Π·Π°Π½Ρ‹ Π² Ρ‚Π°Π±Π»ΠΈΡ†Π°Ρ….
    2. ΠŸΡ€ΠΎΡ‚Π°ΡΠΊΠΈΠ²Π°Π½ΠΈΠ΅ всСх ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ Ρ‡Π΅Ρ€Π΅Π· FRC ΠΈ анимация этих ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ Π² интСрфСйсС – Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅, ΡƒΠ΄Π°Π»Π΅Π½ΠΈΠ΅ ΠΈ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ ячССк Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹ – ΠΎΡ‡Π΅Π½ΡŒ рСсурсоёмкий процСсс. ПослС Π½Π΅ΡΠΊΠΎΠ»ΡŒΠΊΠΈΡ… ΠΏΠ°ΠΊΠ΅Ρ‚ΠΎΠ² интСрфСйс практичСски встанСт.

Π§Ρ‚ΠΎ с ΠΊΡΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ΠΌ

Core Data ΠΊΡΡˆΠΈΡ€ΡƒΠ΅Ρ‚ Π΄Π°Π½Π½Ρ‹Π΅ Π½Π° Π½Π΅ΡΠΊΠΎΠ»ΡŒΠΊΠΈΡ… уровнях.

  1. NSManagedObjectContext , Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, Π² ΠΎΡ‚Π²Π΅Ρ‚ Π½Π° запрос Π²Π΅Ρ€Π½Ρ‘Ρ‚ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹, Π½Π΅ ΠΎΠ±Ρ€Π°Ρ‰Π°ΡΡΡŒ ΠΊ своСму ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚ΠΎΡ€Ρƒ, Ссли эти ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹ ΡƒΠΆΠ΅ Ρ€Π°Π½Π΅Π΅ Π±Ρ‹Π»ΠΈ ΠΈΠΌ ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½Ρ‹.
  2. NSPersistentStoreCoordinator Π΄Π΅Ρ€ΠΆΠΈΡ‚ row cache – сырыС Π΄Π°Π½Π½Ρ‹Π΅, ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½Π½Ρ‹Π΅ ΠΎΡ‚ SQLite , ΠΈ ΠΏΡ€ΠΈ ΠΎΠ±Ρ€Π°Ρ‰Π΅Π½ΠΈΠΈ ΠΊ Π½Π΅ΠΌΡƒ Ρ€Π°Π·Π½Ρ‹Ρ… контСкстов Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ Π΄Π°Π½Π½Ρ‹Π΅ ΠΈΠΌΠ΅Π½Π½ΠΎ ΠΈΠ· этого кэша, Π½Π΅ ΠΎΠ±Ρ€Π°Ρ‰Π°ΡΡΡŒ ΠΊ SQLite .
  3. Ну ΠΈ плюс Ρƒ самой Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ SQLite Π΅ΡΡ‚ΡŒ свой собствСнный кэш.

Как ΠΎΡ‚Π»Π°ΠΆΠΈΠ²Π°Ρ‚ΡŒ

Product / Scheme / Edit Scheme / Run / Arguments. Π’ Arguments Passed On Launch Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ:

  • -com.apple.CoreData.SQLDebug 1 – Π²ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ Π»ΠΎΠ³ запросов Core Data ΠΊ SQLite .
  • -com.apple.CoreData.ConcurrencyDebug 1 – Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹ΠΊΠΈΠ΄Ρ‹Π²Π°Ρ‚ΡŒ exception ΠΏΡ€ΠΈ ΠΏΠΎΠΏΡ‹Ρ‚ΠΊΠ΅ доступа ΠΊ свойствам NSManagedObject ΠΈΠ· Π½Π΅ Ρ€ΠΎΠ΄Π½ΠΎΠ³ΠΎ Π΅ΠΌΡƒ ΠΏΠΎΡ‚ΠΎΠΊΠ°. Π›ΠΎΠ²ΠΈΡ‚ΡŒ exceptionΡ‹ ΡƒΠ΄ΠΎΠ±Π½ΠΎ Ρ‚Π°ΠΊ: View / Navigators / Show Breakpoint Navigator / ΠΊΠ½ΠΎΠΏΠΊΠ° с плюсом справа Π²Π½ΠΈΠ·Ρƒ / Add Exception Breakpoint… / ΠŸΡ€Π°Π²ΠΎΠΉ ΠΊΠ½ΠΎΠΏΠΊΠΎΠΉ ΠΏΠΎ Π½Π΅ΠΌΡƒ / Move Breakpoint To / User – ΠΈ эта Ρ‚ΠΎΡ‡ΠΊΠ° прСрывания Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠΎΡΠ²Π»ΡΡ‚ΡŒΡΡ Π²ΠΎ всСх ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°Ρ….

Product / Profile / инструмСнт Core Data – ΠΏΠΎΠ΄Ρ€ΠΎΠ±Π½ΠΎ Ρ‚ΡƒΡ‚ , Ρ‚Π°ΠΌ Π΅ΡΡ‚ΡŒ субтитры.

  • ΠΎΠΏΡΡ‚ΡŒ ΠΆΠ΅ ΠΈΠ· Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π°Ρ†ΠΈΠΈ ΠΏΠΎ NSFetchedResultsController : A fetch request. This must contain at least one sort descriptor to order the results. – Max Mikheyenko
  • Π”Π°, Ссли ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ FRC. Для получСния количСства ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² – ΠΌΠΎΠΆΠ½ΠΎ Π½Π΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ. – bteapot
  • Π² вопросС Π²Ρ€ΠΎΠ΄Π΅ ΠΈΠΌΠ΅Π½Π½ΠΎ это ΠΈ происходит – Max Mikheyenko
  • Π—Π°Π΄Π°Ρ‡Π° – "Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ количСство". Π—Π°Ρ‡Π΅ΠΌ Ρ‚ΡƒΡ‚ FRC? – bteapot
  • посмотритС ΠΊΠΎΠ΄ Π² вопросС – Max Mikheyenko