I am working on an app that uses Core Data to manage its persistent data. We have been making sufficiently complex schema changes that the lightweight automatic migrations are not compatible, and so we are using manually defined model migrations instead.
My problem is that when I call addPersistentStoreWithType:configuration:URL:options:error: with the NSMigratePersistentStoresAutomaticallyOption set, it is selecting the wrong mapping model. If I call mappingModelFromBundles:forSourceModel:destinationModel: manually, I get the same (incorrect) mapping model returned.
We have three versions of the data model: 1.0, 1.1, and 1.1.1.
We have defined three mapping models: 1.0-1.1, 1.0-1.1.1, and 1.1-1.1.1.
The version of the data in the app is 1.1
The current version of the data is 1.1.1
The mapping model selected is 1.0-1.1.1.
This results in a table that was added in 1.1 being dropped and re-created, losing data.
When I use the -com.apple.CoreData.MigrationDebug 1 command line option, it prints out debugging information for the upgrade process. It shows all the tables in the current dataset (including the one added in 1.1) and their hashes. It then shows the source and destination hashes for the 1.0-1.1.1 mapping model, states that it is a compatible mapping model, and never examines any of the others.
If I use [NSMappingModel initWithContentsOfURL] to specifically select the 1.1-1.1.1 mapping model, I get the desired behaviour.
I'm at a bit of a loss, since it seems to me that it should not be willing to use a mapping model where the source hash does not match the hash of the current dataset.
Here is some selected output showing that the table that is being reset is in the database, but is not in the mapping model. I have verified that the only difference between the 1.1 data hash and the 1.0 data hash is that 1.1 had an added table.
2014-07-03 23:03:20.547 XXX[50242:60b] CoreData: annotation: (migration) will attempt automatic schema migration
2014-07-03 23:03:21.212 XXX[50242:60b] CoreData: annotation: (migration) looking for mapping model with source hashes:
{
XXX = <77891dab a2acaff3 e52f71e3 0f8c3d5f eee99f43 f549e3fc 72eddd29 b2af83fd>;
XXX = <6049b4fb 7d8c4e5f 63149e35 5abfd274 1264c2f9 76d13cf3 cc69a23a e29edac8>;
XXX = <cf3bd61a 71c2838c 421c6e50 41abd013 b0c153cb b165a0e6 21d5c352 f29b5743>;
XXX = <fd8c6be5 b97b3827 455a620c 3e6ff6e9 e2e09afd 472b9cbf 07d11e29 d5a52159>;
XXX = <6cf5aac1 67ead46a fbaf8450 11c2c0b9 dcc1e2ae dd3bbf86 06d09b78 4d4b6bbe>;
XXX = <09942be7 56f82126 d48a90b2 e6cf08e7 1fe9c091 1ee7fec8 8d426ca4 a00af268>;
XXX = <40462ca4 098ae4d2 d3e8e7cf a55bc7df ca58c8c9 3aaf8d94 b681080c 63e5683b>;
XXX = <dce53740 e8aba89f ac8180b4 0f297821 d09734a1 8ea3c344 8cb9dd6c d3baf645>;
XXX = <c9f7a2e3 13518dac 5ae5209d 239d1c11 0fd3f11f 5366b7d4 fd3a97d3 3e3d41d1>;
XXX = <e5bf6c2f c0c9d818 e1b4e2cc 9b7a92e3 2cd6bed4 5a98e6c3 53619376 9b3951ef>;
XXX = <5431c046 d30e7f32 c2cc8099 58add1e7 579ad104 a3aa8fc4 846e97d7 af01cc79>;
XXX = <3aef2c65 c9647274 1302fe8a fdca7ce6 cb87c7df 2751a19f bd946707 c8244729>;
THIS_ONE = <1bb3a3b9 857bcdf2 ca573238 a86672b8 0486929e ed0357c8 72879022 e12efe37>;
XXX = <9f627411 6f8c0891 3693eab1 aa45cbd3 0143b28e 1e3584da e6ea2867 554a26ad>;
XXX = <9def8e9f 14dfb358 b5694bce 77759b7c c1901fe1 3e3a163b 80061b51 268089a8>;
XXX = <06d0b355 4fb4ff4b 0adf05d4 8ce0378c 4aa156e9 a09c8a16 a82d8376 1fd2f929>;
XXX = <fb9db76f 350ad944 88e1cf5d 15aaca9c 230355f9 13a2dace 62d5e4fb 0a2ecd7a>;
XXX = <715d9149 7aea98db fdb3a2fa e1682e12 dfe8f63a d09aac57 301be349 91fffd44>;
XXX = <002c8d92 8bc08e6c eb34fe0c a10ef78e ed050a8e 17a86e63 9911adb8 e2c36df1>;
XXX = <362ea015 28a5c834 47b125c1 c460dd62 f0172785 e024b8aa 17dc544f 66871077>;
XXX = <bd06507d f33ee72d d6bba2d5 c29eb8c5 1f87568b 186ab250 7312c0ec 6f2cd09c>;
XXX = <22ff0e46 f56dbc7a e8e92cf6 9090a451 742517ff 7d29838d 0cd41e9e a3615134>;
XXX = <ec7834a0 987c4c5f df40ade2 73075b11 e329a018 94fe47ca 08f7c9ed 95bb4da1>;
XXX = <c3feffd4 8d223692 d314720a 4496b787 871db7d0 31097cff e1225b9b 6275e613>;
XXX = <e5e3c8aa 5267d778 9fd62dc5 884ef416 5f836890 d82fed79 efd3796d bcb58503>;
XXX = <6705e1bf cac0c2ed b040b64f ca1f6e6e 74332890 907ec136 7a99606c e116a946>;
XXX = <856a10a5 18de663a 1860ea0d c0bd9295 769e4a42 99420fb5 02314b22 f39fe1a4>;
XXX = <35f6c30f a146166c 6e132297 bf463c59 756b8071 49aae2d7 ec6b6de8 fb7f7300>;
XXX = <ec2a9e60 6ae28042 a62429e4 b0ec5939 3734e0ac 9a919421 a9fbede2 031b0bf6>;
XXX = <b08fbebb 9100df77 5aba3640 c8237a5b 4ddbed50 fb6cb28c 439c7e37 9b2ccb4a>;
XXX = <95c8cfb8 a1aafabc 90a9231b 0ef15d85 10e30393 5cfd4921 4db4a12f 511c8977>;
XXX = <4e0fcdb8 4fbf9aa3 684875aa c54a4c5d c02020b2 d29212e4 587069d2 eed3aa31>;
XXX = <ad580044 b972d6ab df963bda ad071ba5 9c82aab5 4007f377 bf8858fe b9bc6274>;
XXX = <4fc9af50 0722da5d 18e0b755 63cf2a04 88e8b2d3 e8196ec2 375171b1 ce40fb4e>;
}
2014-07-03 23:03:21.231 XXX[50242:60b] CoreData: annotation: (migration) checking mapping model /Users/jonathan/Library/Application Support/iPhone Simulator/7.1/Applications/XXX/XXX.app/1.0-1.1.1.cdm
source hashes:
{(
<09942be7 56f82126 d48a90b2 e6cf08e7 1fe9c091 1ee7fec8 8d426ca4 a00af268>,
<40462ca4 098ae4d2 d3e8e7cf a55bc7df ca58c8c9 3aaf8d94 b681080c 63e5683b>,
<ec7834a0 987c4c5f df40ade2 73075b11 e329a018 94fe47ca 08f7c9ed 95bb4da1>,
<b08fbebb 9100df77 5aba3640 c8237a5b 4ddbed50 fb6cb28c 439c7e37 9b2ccb4a>,
<77891dab a2acaff3 e52f71e3 0f8c3d5f eee99f43 f549e3fc 72eddd29 b2af83fd>,
<e5e3c8aa 5267d778 9fd62dc5 884ef416 5f836890 d82fed79 efd3796d bcb58503>,
<4e0fcdb8 4fbf9aa3 684875aa c54a4c5d c02020b2 d29212e4 587069d2 eed3aa31>,
<bd06507d f33ee72d d6bba2d5 c29eb8c5 1f87568b 186ab250 7312c0ec 6f2cd09c>,
<5431c046 d30e7f32 c2cc8099 58add1e7 579ad104 a3aa8fc4 846e97d7 af01cc79>,
<ad580044 b972d6ab df963bda ad071ba5 9c82aab5 4007f377 bf8858fe b9bc6274>,
<c9f7a2e3 13518dac 5ae5209d 239d1c11 0fd3f11f 5366b7d4 fd3a97d3 3e3d41d1>,
<3aef2c65 c9647274 1302fe8a fdca7ce6 cb87c7df 2751a19f bd946707 c8244729>,
<6705e1bf cac0c2ed b040b64f ca1f6e6e 74332890 907ec136 7a99606c e116a946>,
<856a10a5 18de663a 1860ea0d c0bd9295 769e4a42 99420fb5 02314b22 f39fe1a4>,
<6049b4fb 7d8c4e5f 63149e35 5abfd274 1264c2f9 76d13cf3 cc69a23a e29edac8>,
<6cf5aac1 67ead46a fbaf8450 11c2c0b9 dcc1e2ae dd3bbf86 06d09b78 4d4b6bbe>,
<715d9149 7aea98db fdb3a2fa e1682e12 dfe8f63a d09aac57 301be349 91fffd44>,
<e5bf6c2f c0c9d818 e1b4e2cc 9b7a92e3 2cd6bed4 5a98e6c3 53619376 9b3951ef>,
<fb9db76f 350ad944 88e1cf5d 15aaca9c 230355f9 13a2dace 62d5e4fb 0a2ecd7a>,
<35f6c30f a146166c 6e132297 bf463c59 756b8071 49aae2d7 ec6b6de8 fb7f7300>,
<9def8e9f 14dfb358 b5694bce 77759b7c c1901fe1 3e3a163b 80061b51 268089a8>,
<9f627411 6f8c0891 3693eab1 aa45cbd3 0143b28e 1e3584da e6ea2867 554a26ad>,
<dce53740 e8aba89f ac8180b4 0f297821 d09734a1 8ea3c344 8cb9dd6c d3baf645>,
<95c8cfb8 a1aafabc 90a9231b 0ef15d85 10e30393 5cfd4921 4db4a12f 511c8977>,
<cf3bd61a 71c2838c 421c6e50 41abd013 b0c153cb b165a0e6 21d5c352 f29b5743>,
<ec2a9e60 6ae28042 a62429e4 b0ec5939 3734e0ac 9a919421 a9fbede2 031b0bf6>,
<c3feffd4 8d223692 d314720a 4496b787 871db7d0 31097cff e1225b9b 6275e613>,
<06d0b355 4fb4ff4b 0adf05d4 8ce0378c 4aa156e9 a09c8a16 a82d8376 1fd2f929>,
<4fc9af50 0722da5d 18e0b755 63cf2a04 88e8b2d3 e8196ec2 375171b1 ce40fb4e>,
<002c8d92 8bc08e6c eb34fe0c a10ef78e ed050a8e 17a86e63 9911adb8 e2c36df1>,
<fd8c6be5 b97b3827 455a620c 3e6ff6e9 e2e09afd 472b9cbf 07d11e29 d5a52159>,
<362ea015 28a5c834 47b125c1 c460dd62 f0172785 e024b8aa 17dc544f 66871077>,
<22ff0e46 f56dbc7a e8e92cf6 9090a451 742517ff 7d29838d 0cd41e9e a3615134>
)}
2014-07-03 23:03:21.233 XXX[50242:60b] CoreData: annotation: (migration) found compatible mapping model /Users/jonathan/Library/Application Support/iPhone Simulator/7.1/Applications/XXX/XXX.app/1.0-1.1.1.cdm
I'm using iOS 7.1 and Xcode 5.1.1.
As mentioned, the only change between 1.0 and 1.1 appears to be the addition of a table. I guess the hash comparison function they use does not consider this to be a conflict? My next attempt will be to rename the migrations on the assumption that an alphabetical search is being used so that the 1.1-1.1.1 migration is found and checked first. Other than that I expect I will have to add some sort of manual logic (by subclassing something?) to force the additional table to be treated as a schema mismatch.
This is not the answer as to why the automatic selection of the mapping model fails. As I wrote in my comment I did encountered a similar issue and wrote my own (manual) mapping model selection.
For the manual selection we need to know which model was use to create the current store.
All Entities (tables) have a version hash, so this hash can be used to identify the model version.
After the app starts up, but before loading the persistent store the mapping model selection loops through the possible data models, i.e. 1.0.xcdatamodel, 1.1.xcdatamodel, and 1.1.1.xcdatamodel
The logic compares the known hash of the entity THIS_ONE against the version hash found in the persistent store. The known version hash THIS_ONE comes from the data model file. If the data model was used for creating the store then the hashes match.
So the app loops through a list of known model names (i.e. "1.0", "1.1" and "1.1.1") and calls the matching method isModel:forStore:. If the return is YES then we have found the matching model.
Now that we know the source data model we can identify the mapping model. Next step would be to kick of the actual migration using the appropriate mapping file for
migrateStoreFromURL:type:options:withMappingModel:toDestinationURL:destinationType:destinationOptions:error:
Here is the method to match the version hashes:
-(BOOL)isModel:(NSString *)modelUsed forStore:(NSURL *)storeUrl {
NSString *modelFound = #"unknown Model";
NSDictionary *knownTHIS_ONEHashes = [self knownTHIS_ONEHashes];
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL doesExistCurrentStore = [fileManager fileExistsAtPath:[storeUrl path]];
if (doesExistCurrentStore) {
NSError *error = nil;
NSDictionary *storeMetadata = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:NSSQLiteStoreType URL:storeUrl error:&error];
if (nil == storeMetadata) { // no source meta data => dont know if need to migrate
NSLog(#"sourceMetadata is nil");
} else {
NSLog(#"sourceMetadata is %#", storeMetadata);
NSDictionary *storeHashes = [storeMetadata objectForKey:#"NSStoreModelVersionHashes"];
NSData *curentTHIS_ONEHash = storeHashes[#"THIS_ONE"];
for (NSString *modelName in [knownTHIS_ONEHashes allKeys]) {
if ([knownTHIS_ONEHashes[modelName] isEqualToData:curentTHIS_ONEHash]) {
NSLog(#"found matching model: %#",modelName);
modelFound = modelName;
break;
}
}
}
} // else store does not exist so there is no need for a data migration
return ([modelUsed isEqualToString:modelFound]);
}
The known hashes are read at run time form the models present in the bundle. _kModelNameVx are the hard coded model names.
-(NSDictionary *)knownTHIS_ONEHashes {
NSMutableDictionary *returnDict = [NSMutableDictionary new];
NSArray *knowModelFiles = #[_kModelNameV1, _kModelNameV2, _kModelNameV3, _kModelNameV4];
NSString * destinationModelPath;
NSURL * destinationModelURL;
NSManagedObjectModel * destinationModel;
for (NSString *singleFile in knowModelFiles) {
destinationModelPath = [[NSBundle mainBundle] pathForResource:singleFile
ofType:#"mom"
inDirectory:#"<xcdatamodel_name>.momd"];
destinationModelURL = [NSURL fileURLWithPath:destinationModelPath];
destinationModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:destinationModelURL];
NSDictionary *modelMetadata = [destinationModel entityVersionHashesByName];
NSData *THIS_ONEHash = [modelMetadata objectForKey:#"THIS_ONE"];
[returnDict setValue:THIS_ONEHash forKey:singleFile];
}
return returnDict;
}
My friends, I have an issue that I hope you can help me.
Here is a similar problem, but it is also not solved:
Reading 'CSV' file - Cannot read csv file contains loong string
So...Here is my code:
NSString* filePath = [[NSBundle mainBundle] pathForResource:#"nameorg" ofType:#"csv"];
NSString* fileContents = [NSString stringWithContentsOfFile:filePath encoding:NSWindowsCP1251StringEncoding error:nil];
NSMutableArray* pointStrings = [[NSMutableArray alloc] initWithArray:[fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]];
for(int idx = 0; idx < [pointStrings count]; idx++)
{
NSString *currentPointString = [pointStrings objectAtIndex:idx];
if ([currentPointString length] == 0) {
continue;
}
NSMutableArray *arr = [currentPointString componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#";"]];
if ([arr count] < 5) {
continue;
}
NSLog(#"1: %#; 2: %#; 3: %#; 4: %#; 5: %#;", [arr objectAtIndex:0], [arr objectAtIndex:1], [arr objectAtIndex:2], [arr objectAtIndex:3], [arr objectAtIndex:4]);
}
My csv file:
2.6.;¬ качестве адреса организации можно использовать адрес офиса учредител€, в том числе домашний адрес руководител€ фирмы, адрес арендованного помещени€.;;The address of administrative office, including home address of the company administrator, the address of leased premises, may be used as address of the organization.;
2.7.;Ёлектронна€ подача с помощью сервиса Ђѕодача электронных документов на государственную регистрациюї http://www.nalog.ru/el_usl/gosreg_eldocs/;"1. «аполнить в интерактивном режиме за€вление и направить его в налоговую инспекцию.
2. ѕодготовить полный пакет документов в электронном виде и направить его в налоговый орган.";E-applications submission through the service ЂElectronic submission of documents for state registrationї†http://www.nalog.ru/el_usl/gosreg_eldocs/;"1 . To fill an application online and submit it to tax service.
2 . To prepare a full set of documents in electronic form and send it to tax registration authority."
2.8.;¬ бумажном виде;;In paper form;
The problem: Not displayed item 2.7. Why?
My NSLog:
2014-02-11 14:03:34.139 CSVSQLITE[4127:70b] 1: 2.6.; 2: В качестве адреса организации можно использовать адрес офиса учредителя, в том числе домашний адрес руководителя фирмы, адрес арендованного помещения.; 3: ; 4: The address of administrative office, including home address of the company administrator, the address of leased premises, may be used as address of the organization.; 5: ;
2014-02-11 14:03:34.163 CSVSQLITE[4127:70b] 1: 2.8.; 2: В бумажном виде; 3: ; 4: In paper form; 5: ;
Thanks so much in advanced.
output of pointStrings:
(
"2.6.;\U0412 \U043a\U0430\U0447\U0435\U0441\U0442\U0432\U0435 \U0430\U0434\U0440\U0435\U0441\U0430 \U043e\U0440\U0433\U0430\U043d\U0438\U0437\U0430\U0446\U0438\U0438 \U043c\U043e\U0436\U043d\U043e \U0438\U0441\U043f\U043e\U043b\U044c\U0437\U043e\U0432\U0430\U0442\U044c \U0430\U0434\U0440\U0435\U0441 \U043e\U0444\U0438\U0441\U0430 \U0443\U0447\U0440\U0435\U0434\U0438\U0442\U0435\U043b\U044f, \U0432 \U0442\U043e\U043c \U0447\U0438\U0441\U043b\U0435 \U0434\U043e\U043c\U0430\U0448\U043d\U0438\U0439 \U0430\U0434\U0440\U0435\U0441 \U0440\U0443\U043a\U043e\U0432\U043e\U0434\U0438\U0442\U0435\U043b\U044f \U0444\U0438\U0440\U043c\U044b, \U0430\U0434\U0440\U0435\U0441 \U0430\U0440\U0435\U043d\U0434\U043e\U0432\U0430\U043d\U043d\U043e\U0433\U043e \U043f\U043e\U043c\U0435\U0449\U0435\U043d\U0438\U044f.;;The address of administrative office, including home address of the company administrator, the address of leased premises, may be used as address of the organization.;",
"",
"2.7.;\U042d\U043b\U0435\U043a\U0442\U0440\U043e\U043d\U043d\U0430\U044f \U043f\U043e\U0434\U0430\U0447\U0430 \U0441 \U043f\U043e\U043c\U043e\U0449\U044c\U044e \U0441\U0435\U0440\U0432\U0438\U0441\U0430 \U00ab\U041f\U043e\U0434\U0430\U0447\U0430 \U044d\U043b\U0435\U043a\U0442\U0440\U043e\U043d\U043d\U044b\U0445 \U0434\U043e\U043a\U0443\U043c\U0435\U043d\U0442\U043e\U0432 \U043d\U0430 \U0433\U043e\U0441\U0443\U0434\U0430\U0440\U0441\U0442\U0432\U0435\U043d\U043d\U0443\U044e \U0440\U0435\U0433\U0438\U0441\U0442\U0440\U0430\U0446\U0438\U044e\U00bb http://www.nalog.ru/el_usl/gosreg_eldocs/;\"1. \U0417\U0430\U043f\U043e\U043b\U043d\U0438\U0442\U044c \U0432 \U0438\U043d\U0442\U0435\U0440\U0430\U043a\U0442\U0438\U0432\U043d\U043e\U043c \U0440\U0435\U0436\U0438\U043c\U0435 \U0437\U0430\U044f\U0432\U043b\U0435\U043d\U0438\U0435 \U0438 \U043d\U0430\U043f\U0440\U0430\U0432\U0438\U0442\U044c \U0435\U0433\U043e \U0432 \U043d\U0430\U043b\U043e\U0433\U043e\U0432\U0443\U044e \U0438\U043d\U0441\U043f\U0435\U043a\U0446\U0438\U044e.",
"2. \U041f\U043e\U0434\U0433\U043e\U0442\U043e\U0432\U0438\U0442\U044c \U043f\U043e\U043b\U043d\U044b\U0439 \U043f\U0430\U043a\U0435\U0442 \U0434\U043e\U043a\U0443\U043c\U0435\U043d\U0442\U043e\U0432 \U0432 \U044d\U043b\U0435\U043a\U0442\U0440\U043e\U043d\U043d\U043e\U043c \U0432\U0438\U0434\U0435 \U0438 \U043d\U0430\U043f\U0440\U0430\U0432\U0438\U0442\U044c \U0435\U0433\U043e \U0432 \U043d\U0430\U043b\U043e\U0433\U043e\U0432\U044b\U0439 \U043e\U0440\U0433\U0430\U043d.\";E-applications submission through the service \U00abElectronic submission of documents for state registration\U00bb\U00a0http://www.nalog.ru/el_usl/gosreg_eldocs/;\"1 . To fill an application online and submit it to tax service.",
"",
"2 . To prepare a full set of documents in electronic form and send it to tax registration authority.\"",
"",
"2.8.;\U0412 \U0431\U0443\U043c\U0430\U0436\U043d\U043e\U043c \U0432\U0438\U0434\U0435;;In paper form;",
"",
)
output of arr:
2014-02-11 14:07:58.545 CSVSQLITE[4249:70b] arr: (
"2.6.",
"\U0412 \U043a\U0430\U0447\U0435\U0441\U0442\U0432\U0435 \U0430\U0434\U0440\U0435\U0441\U0430 \U043e\U0440\U0433\U0430\U043d\U0438\U0437\U0430\U0446\U0438\U0438 \U043c\U043e\U0436\U043d\U043e \U0438\U0441\U043f\U043e\U043b\U044c\U0437\U043e\U0432\U0430\U0442\U044c \U0430\U0434\U0440\U0435\U0441 \U043e\U0444\U0438\U0441\U0430 \U0443\U0447\U0440\U0435\U0434\U0438\U0442\U0435\U043b\U044f, \U0432 \U0442\U043e\U043c \U0447\U0438\U0441\U043b\U0435 \U0434\U043e\U043c\U0430\U0448\U043d\U0438\U0439 \U0430\U0434\U0440\U0435\U0441 \U0440\U0443\U043a\U043e\U0432\U043e\U0434\U0438\U0442\U0435\U043b\U044f \U0444\U0438\U0440\U043c\U044b, \U0430\U0434\U0440\U0435\U0441 \U0430\U0440\U0435\U043d\U0434\U043e\U0432\U0430\U043d\U043d\U043e\U0433\U043e \U043f\U043e\U043c\U0435\U0449\U0435\U043d\U0438\U044f.",
"",
"The address of administrative office, including home address of the company administrator, the address of leased premises, may be used as address of the organization.",
""
)
2014-02-11 14:07:58.548 CSVSQLITE[4249:70b] arr: (
"2.7.",
"\U042d\U043b\U0435\U043a\U0442\U0440\U043e\U043d\U043d\U0430\U044f \U043f\U043e\U0434\U0430\U0447\U0430 \U0441 \U043f\U043e\U043c\U043e\U0449\U044c\U044e \U0441\U0435\U0440\U0432\U0438\U0441\U0430 \U00ab\U041f\U043e\U0434\U0430\U0447\U0430 \U044d\U043b\U0435\U043a\U0442\U0440\U043e\U043d\U043d\U044b\U0445 \U0434\U043e\U043a\U0443\U043c\U0435\U043d\U0442\U043e\U0432 \U043d\U0430 \U0433\U043e\U0441\U0443\U0434\U0430\U0440\U0441\U0442\U0432\U0435\U043d\U043d\U0443\U044e \U0440\U0435\U0433\U0438\U0441\U0442\U0440\U0430\U0446\U0438\U044e\U00bb http://www.nalog.ru/el_usl/gosreg_eldocs/",
"\"1. \U0417\U0430\U043f\U043e\U043b\U043d\U0438\U0442\U044c \U0432 \U0438\U043d\U0442\U0435\U0440\U0430\U043a\U0442\U0438\U0432\U043d\U043e\U043c \U0440\U0435\U0436\U0438\U043c\U0435 \U0437\U0430\U044f\U0432\U043b\U0435\U043d\U0438\U0435 \U0438 \U043d\U0430\U043f\U0440\U0430\U0432\U0438\U0442\U044c \U0435\U0433\U043e \U0432 \U043d\U0430\U043b\U043e\U0433\U043e\U0432\U0443\U044e \U0438\U043d\U0441\U043f\U0435\U043a\U0446\U0438\U044e."
)
2014-02-11 14:07:58.549 CSVSQLITE[4249:70b] arr: (
"2. \U041f\U043e\U0434\U0433\U043e\U0442\U043e\U0432\U0438\U0442\U044c \U043f\U043e\U043b\U043d\U044b\U0439 \U043f\U0430\U043a\U0435\U0442 \U0434\U043e\U043a\U0443\U043c\U0435\U043d\U0442\U043e\U0432 \U0432 \U044d\U043b\U0435\U043a\U0442\U0440\U043e\U043d\U043d\U043e\U043c \U0432\U0438\U0434\U0435 \U0438 \U043d\U0430\U043f\U0440\U0430\U0432\U0438\U0442\U044c \U0435\U0433\U043e \U0432 \U043d\U0430\U043b\U043e\U0433\U043e\U0432\U044b\U0439 \U043e\U0440\U0433\U0430\U043d.\"",
"E-applications submission through the service \U00abElectronic submission of documents for state registration\U00bb\U00a0http://www.nalog.ru/el_usl/gosreg_eldocs/",
"\"1 . To fill an application online and submit it to tax service."
)
2014-02-11 14:07:58.550 CSVSQLITE[4249:70b] arr: (
"2 . To prepare a full set of documents in electronic form and send it to tax registration authority.\""
)
2014-02-11 14:07:58.551 CSVSQLITE[4249:70b] arr: (
"2.8.",
"\U0412 \U0431\U0443\U043c\U0430\U0436\U043d\U043e\U043c \U0432\U0438\U0434\U0435",
"",
"In paper form",
""
)
2.7 is not displaying because of the following if condition.
if ([arr count] < 5)
{
continue;
}
The 2.7 String contains only two ;
NSMutableArray *arr = [currentPointString componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#";"]];
It'll contain only 3 objects, so the if condition is satisfied.
I have an issue with my app where it spikes up to over 100% CPU usage when gathering data. I have done everything I can think of to reign in the processing so that it doesn't lock the device up, and still continues to get the data that's needed.
Our customers have some big databases, so downloading the entire database is out the question. We are also using REMObjects as middleware, so I have to work with that. What I did was figure out what data was needed for the user, and setup a series of calls to gather that information. I think that where my problem lies is in the fact that the database can only process up to 1500 items in a call.
Here is a sample query string that is getting sent to the server.
SELECT COMMUNICATIONID, PHONEEXTENTION, PHONEDESC, PHONETIMETOCALL, PHONENUMBER
FROM PHONE WHERE COMMUNICATIONID IN (3761, 3793, 5530, 4957, 4320, 1914, 3715, 6199, 5548,
5580, 5994, 5867, 1437, 4943, 6217, 3765, 2442, 227, 4084, 977, 6822, 5680, 263, 4502,
327, 6112, 136, 7053, 5571, 6958, 6799, 5525, 6530, 4779, 604, 2182, 6198, 3792, 6071,
4383, 5866, 7444, 1309, 226, 4083, 5916, 1295, 626, 1249, 1950, 2141, 3369, 326, 135,
6780, 5411, 5938, 4424, 6034, 649, 6179, 5861, 4778, 5479, 2181, 6197, 3791, 5815, 6070,
6420, 7935, 4542, 4319, 6679, 4942, 4082, 4974, 5533, 5788, 5597, 976, 3764, 1917, 6202,
134, 6779, 3768, 5410, 5665, 7880, 7052, 6033, 5492, 6815, 3118, 4218, 5110, 6529, 6115,
6069, 348, 4318, 4382, 1498, 6406, 4941, 7443, 2376, 4623, 5755, 5532, 6201, 6392, 625,
7270, 4977, 6396, 6524, 5664, 7051, 725, 6032, 6701, 6160, 5491, 5937, 6273, 1875, 6114,
5477, 6528, 5573, 4936, 6705, 2180, 3758, 5527, 5368, 5814, 7328, 7424, 429, 5991, 1434,
6391, 6200, 7283, 5868, 5900, 228, 4085, 6109, 1106, 5791, 692, 6095, 7210, 2893, 1188,
6814, 4217, 5572, 3757, 5813, 3694, 796, 605, 6486, 128, 4144, 5722, 5754, 1915, 5676,
5549, 5581, 4976, 5917, 5822, 2174, 6158, 1633, 4566, 5267, 4885, 4503, 1874, 6113, 5476,
4425, 4871, 5526, 6531, 7886, 1496, 5194, 127, 4780, 5721)
That string is created by the following method, which then sends it asynchronously to the server. There is one issue in this method that I know is a problem, but I haven't had the time to circle back to it and devise a better solution. I am using respondsToSelector and performSelector to process additional methods based on the table that we're gathering details from.
- (void)processRequest
{
if( requestQueue.count == 0 )
return;
if( processingQueue.count > 3 )
return;
Request *request = requestQueue[0];
[requestQueue removeObjectAtIndex:0];
DADataTable *source = request.source;
NSString *destTableName = request.destTableName;
NSString *sourceKey = request.sourceKey;
NSString *query = request.query;
NSArray *destKeys = request.destKeys;
NSString *originMethodName = request.originMethodName;
NSArray *destinationMethods = request.destinationMethods;
NSString *message = request.loadingMessage;
[[NSNotificationCenter defaultCenter] postNotificationName:#"GATHERINGDATA" object:nil];
// Cycle through the rows in the source table and extract the keys we need.
// originMethodName is needed because some tables require additional checks
// to determine what kind of key we are working with
// sourceKey is the string that holds the key we're looking for, which is
// used on tables that don't need specific filtering
NSSet *set = [self getSourceSet:source originMethodName:originMethodName sourceKey:sourceKey];
// getLists takes the set generated by getSourceSet and converts the list of
// ids into a comma separated list of items suitable for passing into a query
// Currently there is a 1400 item limit per list to keep from exceeding the server
// limit, which is currently 1500
NSArray *lists = [self getLists:set];
NSString *msg = #"Loading Data";
NSLog(#"%#", message);
for( NSString *tList in lists ) {
if( tList.length == 0 ) {
NSLog(#"%# not needed", originMethodName);
continue;
}
query = [query stringByAppendingFormat:#"%#)", tList];
NSLog(#"%#: %#", destTableName, query);
DAAsyncRequest __block *r = [fda beginGetDataTableWithSQL:query withBlock:^(DADataTable *table){
DADataTable *destination = [tables valueForKey:destTableName];
if( tables.count == 0 ) destination = table;
else if( [destination rowCount] > 0 )
//dispatch_async(queue, ^(){
[destination mergeTable:table withPrimaryKeys:destKeys];
//});
else
destination = table;
[[NSUserDefaults standardUserDefaults] setValue:msg forKey:#"LoadingMessage"];
[[NSNotificationCenter defaultCenter] postNotificationName:InitialViewControllerNotificationLoadingUpdate object:nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"UpdateStatus" object:nil];
//dispatch_async(queue, ^(){
[tables setValue:destination forKey:destTableName];
//});
for( NSString *method in destinationMethods) {
SEL tMethod = NSSelectorFromString(method);
if( [self respondsToSelector:tMethod]) {
[self performSelector:tMethod withObject:table];
}
}
if( [self busy] &&
[[source name] isEqualToString:DataAccessTableCustomer])
{
[[NSUserDefaults standardUserDefaults] setValue:nil forKey:#"FETCHINGJOB"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"JOBFETCHED" object:nil];
}
if( [[[NSUserDefaults standardUserDefaults] valueForKey:#"FETCHINGCONTACT"] isEqualToString:#"YES"] &&
([[source name] isEqualToString:DataAccessTablePerson] ||
[[source name] isEqualToString:DataAccessTableOrganization] ||
[[source name] isEqualToString:DataAccessTableOrganizationAddress]))
{
[[NSUserDefaults standardUserDefaults] setValue:nil forKey:#"FETCHINGCONTACT"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"CONTACTFETCHED" object:nil];
}
[processingQueue removeObject:r];
[[NSNotificationCenter defaultCenter] postNotificationName:#"PROCESSREQUEST" object:nil];
}];
[processingQueue addObject:r];
}
}
Any help here will be greatly appreciated! Thanks for taking the time to look.
Yes. Basically the golden rule is: do not optimize prematurely.
But anyhow. My first guess would be: replace NSString query with NSMutableString query. Because you are creating 1500 NSString objects on the heap with always increasing length, just to throw them away in the next loop. An NSMutalbeString keeps up the memory for a much longer time when appending - and you are always talking to the same object. '(Then use appendFormat without the re-assignment instead of stringByAppendingFormat with assignment!)
Look here: Is NSMutableString's -appendString: method an efficient way to build up a large string?
Having your CPU climb to over 100% is not necessarily a bad thing. On a device with multiple cores (iPhone 4s and later) the CPU utilization is 100% times the number of cores. So on a 4s max is 200%, and on a 5s it's 400%.
Doing a bunch of processing in a loop maximizes CPU usage on that thread, since the code runs at full speed until it's done. That's normal and appropriate.
Are you seeing laggy UI performance? That's what you should use as your gauge that you need to improve things.
My suggestion would be to rewrite your code to run on a GCD background queue. First try default priority (the same priority as the UI.) On a multi-core machine, that will tie up one of the cores. On an iPhone 4, though, it might make the UI laggy. In that case you could switch to the next-lower priority, which would make it take longer, but give a higher priority to the UI.
You could then optimize your code if needed. User defaults is not the most efficient way to handle state data in a loop. You might try removing the user defaults calls and switch to saving data in an instance variable or, in a data container singleton if you need to pass info between objects. Also NSNotificationCenter has more overhead than delegate calls, block calls, or simple method calls.
However, I would not worry about those things until you determine that optimization is needed.