iOS崩溃使用dispatch_sync

时间:2017-09-16 22:30:39

标签: ios objective-c synchronization dispatch

在我的iOS应用程序中,我读取了一个大型CSV文件并将其加载(通过插入)在SQLite数据库中。

这是我的代码:

- (void) parseCSVFile:(NSString *)path
{
    __block BOOL isFileOk = NO;
    __block BOOL atLeastOneRowProcessed = NO;

    NSFileManager *fileMgr = [[NSFileManager alloc] init];
    unsigned long totalSize = [[[fileMgr attributesOfItemAtPath:path error:nil] objectForKey:@"NSFileSize"] intValue];

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:@"mydatabase.sqlite"];
    const char *dbpath = [writableDBPath UTF8String];

    dispatch_queue_t databaseQueue = [(AppDelegate *)[[UIApplication sharedApplication] delegate] databaseQueue];
    dispatch_sync(databaseQueue, ^{

        sqlite3 *ddbb;
        NSLog(@"INIT DATABASE");
        if (sqlite3_open(dbpath, &ddbb) == SQLITE_OK)
        {
            char* errorMessage;
            sqlite3_exec(ddbb, "DROP TABLE IF EXISTS datainfile", NULL, NULL, &errorMessage);
            sqlite3_exec(ddbb, "CREATE TABLE \"datainfile\" (\"field1\" TEXT PRIMARY KEY  NOT NULL ,\"field2\" TEXT DEFAULT (null) ,\"field3\" TEXT DEFAULT (null) ,\"field4\" TEXT)", NULL, NULL, &errorMessage);
            sqlite3_exec(ddbb, "BEGIN TRANSACTION", NULL, NULL, &errorMessage);
            char buffer[] = "INSERT OR IGNORE INTO datainfile VALUES (?1, ?2, ?3, ?4)";
            sqlite3_stmt* stmt;
            sqlite3_prepare_v2(ddbb, buffer, (int)strlen(buffer), &stmt, NULL);

            char *pathUTF8 = (char*)[path UTF8String];
            FILE* stream = fopen(pathUTF8, "r");
            char *field1 = "";
            char *field2 = "";
            char *field3 = "";
            char *field4 = "";

            NSLog(@"READING FILE");
            unsigned long processedSize = 0;
            char line[1024];
            while(fgets(line, 1024, stream))
            {
                processedSize = processedSize + strlen(line) * sizeof(char);

                char* tmp = strdup(line);
                field1 = strtok (tmp,", \t");
                field2 = strtok (NULL, ", \t");
                field3 = strtok (NULL, ", \t");
                field4 = strtok (NULL, "\r\n");

                atLeastOneRowProcessed = YES;
                if(!isFileOk)
                {
                    isFileOk = [self checkIfFileIsACorrectCSVWithField1:field1 field2:field2 field3:field3 andField4:field4];

                    if(!isFileOk)
                    {
                        if([_delegate respondsToSelector:@selector(fileParserError:)])
                            [_delegate fileParserError:_reportErrorMessage];
                        free(tmp);
                        break;
                    }
                }

                sqlite3_bind_text(stmt, 1, field1, -1, SQLITE_STATIC);
                sqlite3_bind_text(stmt, 2, field2, -1, SQLITE_STATIC);
                sqlite3_bind_text(stmt, 3, field3, -1, SQLITE_STATIC);
                sqlite3_bind_text(stmt, 4, field4, -1, SQLITE_STATIC);

                if (sqlite3_step(stmt) != SQLITE_DONE)
                {
                    NSLog(@"ERROR: Commit Failed in line %s!\n", line);
                    // If there was an error we try to repeat the commit
                    sqlite3_reset(stmt);
                    sqlite3_bind_text(stmt, 1, field1, -1, SQLITE_STATIC);
                    sqlite3_bind_text(stmt, 2, field2, -1, SQLITE_STATIC);
                    sqlite3_bind_text(stmt, 3, field3, -1, SQLITE_STATIC);
                    sqlite3_bind_text(stmt, 4, field4, -1, SQLITE_STATIC);
                    if (sqlite3_step(stmt) != SQLITE_DONE)
                    {
                        NSLog(@"ERROR: Commit failed again");
                    }
                    else
                    {
                        NSLog(@"ERROR: Commit success");
                    }
                }
                sqlite3_reset(stmt);
                free(tmp);
            }
            if(isFileOk)
            {
                NSLog(@"File read and now the commit starts");
                sqlite3_exec(ddbb, "COMMIT TRANSACTION", NULL, NULL, &errorMessage);

                _phase = PARSER_PHASE_CREATING_INDEX;

                sqlite3_exec(ddbb, "CREATE INDEX field1 ON datainfile(field1);", NULL, NULL, &errorMessage);
                sqlite3_finalize(stmt);
                NSLog(@"Commit has ended");
            }
        }
        sqlite3_close(ddbb);

    });

    if(isFileOk)
    {
             if([_delegate respondsToSelector:@selector(fileParserFinished)])
                [_delegate fileParserFinished];
    }
    else
    {
        if(!atLeastOneRowProcessed)
        {
            _reportErrorMessage = NSLocalizedString(@"TXT_FILEERROR_NOPROCESSED", Nil);
            if([_delegate respondsToSelector:@selector(fileParserError:)])
                [_delegate fileParserError:_reportErrorMessage];
        }
    }
}

databaseQueue是以这种方式创建的AppDelegate的一个属性:

_databaseQueue = dispatch_queue_create("com.mycompany.myapp.database", 0);

嗯,这几乎总是很好用。但有时它会发生奇怪的崩溃。这是日志:

Crashed: com.mycompany.myapp.database
0  libsystem_kernel.dylib         0x1865d1014 __pthread_kill + 8
1  libsystem_pthread.dylib        0x18669b264 pthread_kill + 112
2  libsystem_c.dylib              0x1865459c4 abort + 140
3  libsystem_c.dylib              0x186545ad8 _UTF2_init + 82
4  libsystem_c.dylib              0x186559c7c __chk_fail_overlap + 54
5  libsystem_c.dylib              0x186559c44 __chk_fail + 18
6  libsystem_c.dylib              0x18655a04c __vsprintf_chk + 134
7  MyApp                          0x1000c151c __32-[FileParser parseCSVFile:]_block_invoke (FileParser.m:650)
8  libdispatch.dylib              0x18648e9a0 _dispatch_client_callout + 16
9  libdispatch.dylib              0x18649bee0 _dispatch_barrier_sync_f_invoke + 84
10 MyApp                          0x1000c1084 -[FileParser parseCSVFile:] (FileParser.m:734)
11 MyApp                          0x1000bed28 -[FileParser readFileWithPath:andDelegate:] (FileParser.m:147)
12 MyApp                          0x100041a64 __57-[HomeViewController readFileWithPath:]_block_invoke_2 (HomeViewController.m:625)
13 libdispatch.dylib              0x18648e9e0 _dispatch_call_block_and_release + 24
14 libdispatch.dylib              0x18648e9a0 _dispatch_client_callout + 16
15 libdispatch.dylib              0x18649d0d4 _dispatch_queue_override_invoke + 644
16 libdispatch.dylib              0x18649ea50 _dispatch_root_queue_drain + 540
17 libdispatch.dylib              0x18649e7d0 _dispatch_worker_thread3 + 124
18 libsystem_pthread.dylib        0x186697100 _pthread_wqthread + 1096
19 libsystem_pthread.dylib        0x186696cac start_wqthread + 4

它发生在我的FileParser的第734行和第650行,这些行是:

(第734行)第二个 if(isFileOk)

离开dispatch_sync后的第一行。

(第650行) processedSize = processedSize + strlen(line)* sizeof(char);

阅读CSV文件的第一行。

在我看来,dispatch_sync在执行之前会保留代码,同时调用块外的一行和块内的一行。

我必须补充一点,当发生此崩溃时,在块外部崩溃的行总是 if(isFileOk),但是在块内崩溃的行会有所不同。

它会发生什么?

0 个答案:

没有答案