📜 ⬆️ ⬇️

Do you precisely initialize the Core Data stack correctly?



Have you ever noticed that any of your favorite iOS apps after the next update has stopped working or has its launch been delayed for half a minute? Usually after this, their developers release an urgent bugfix. And this is not always associated with bugs in the code of the final programmer, sometimes the problem lies a little deeper.

It seems to me rather strange that this error occurs quite often (and should occur in "serious" projects), but for some reason they are silent about it.
This article will discuss the standard error when initializing the CoreData stack in iOS applications.

The CoreData framework is a powerful tool in the hands of Cocoa developers, almost free persistence, ease of data change, writing, versioning, model-to-model migration — all that is so often necessary in our projects. Someone uses it in readonly mode, someone saves quite a bit and works with such a small sample, but someone uses it to its fullest.
')
I will say right away that in order to encounter an error, you need to collect three reasons: a large amount of the database, initialization of the CoreData stack in the main thread, changes to the database schema in the new version of your program. Let's look at how easy they can all be assembled as a developer.


Reason number 1. Large file size base

When using "to the full" it often happens that the size of the database is artificially unlimited and can easily occupy a gigabyte.

For examples of the last do not need to go long. On the Internet, people still wonder whether it is possible to store pictures in a database (by the way, in this article CoreData and the database will be almost synonymous). Quite popular answers with the approval of Stackoverflow-audience say that you can safely store up to a megabyte in the database. For example, stackoverflow.com/questions/2573072/coredata-store-images-to-db-or-not

The answer is:

<100kb store
<1mb store in a separate table
> 1mb store on disk data

Or your application can keep your active correspondence of the user over the past three years (messenger, email client, etc.).
Deal with the reasons of large volumes.

Reason number 2. Initialization in the main thread

Well, can there be any doubt? I think 100% percent of newbies and exactly 70% of developers more experiencedly initialize the entire CoreData stack in the main program flow.

Reason number 3. The need to migrate to a new data scheme

Usually, when changing the database schema (model), CoreData transfers data from the old database to the new one, if you set the appropriate rules. The simplest so-called. Lightweight migration is easy, you need to pass a dictionary of options when connecting the repository to NSPersistentStoreCoordinator:

NSDictionary *optionsDictionary = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil]; [_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:optionsDictionary error:&error]; 


If the transfer does not fit into the framework of lightweight migration, then the developers implement their custom, but the essence of reason number 3 does not change.

Causes collected


Causes collected. It's pretty easy to collect them, isn't it? Well, it’s just as simple these reasons are collected by many developers, who once inserted the initialization code at the dawn of the development of their project and safely forgot about it until they saw something like this in iTunes:

, !

203 203


What's happening?


I think all these reasons give you a simple conclusion. And he is faithful! Even a simple copy of data about migration to a new version of the database takes time, and the more the database file takes, the longer it takes. And we are talking about simple copying, and if you need to re-index the fields, for example?

The bottleneck has already been mentioned - the connection of persistent storage to an NSPersistentStoreCoordinator object. It is here that the combined reasons create a problem. And if your application does not respond for 30 seconds, the system closes it.

Decision


Fortunately, we can create NSPersistentStoreCoordinator and connect storage to it in another thread. And at the time of initialization of data is a good way - to show some window with the inscription "Update data", for example.

This is how it will look in the code (I’ll tell you right away that the solution does not claim to be ideal, if someone thinks better, write in the comments).

 //     GUI    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.launchOptions = launchOptions; [self performSelectorInBackground:@selector(initCoreData) withObject:nil]; [NSThread sleepForTimeInterval:0.2]; // ,       ,    .        [self initialDisplayGUI]; return YES; } - (void)initialDisplayGUI { if (self.dataIsReady) { [self diplayAllGUIStuff]; } else { self.dataPrepareController = [[MigrationProgressViewController alloc] init]; [dataPrepareController setDoneTarget:self withAction:@selector(diplayAllGUIStuff)]; dataPrepareController.view.frame = window.frame; [window addSubview:dataPrepareController.view]; [window makeKeyAndVisible]; } } - (void)initCoreData { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; if (self.persistentStoreCoordinator) { NSLog(@"Storage was added"); } self.dataIsReady = YES; [pool release]; } - (void)setDataIsReady:(BOOL)dataIsReady { if (_dataIsReady != dataIsReady) { _dataIsReady = dataIsReady; [self performSelectorOnMainThread:@selector(diplayAllGUIStuff) withObject:nil waitUntilDone:NO]; } } 


-diplayAllGUIStuff method that contains the code that you had in - (BOOL) application: didFinishLaunchingWithOptions:

The controller MigrationProgressViewController is needed to display, for example, indicators of the remaining time, or at least to show that the process does not hang. His only task is to calm the user. It is more pleasant for users to even look at the “bare” UIActivityIndicatorView than on the dangling splash screen and even more so to have the departing application on hand.

This is probably all. Avoid such coincidences and more often review the code, especially the one that Xcode interposed for you.
image

Source: https://habr.com/ru/post/149266/


All Articles