Фоновая задача с блоком завершения в IOS

Я делаю некоторые операции с базой данных в IOS. В основном я хочу сделать это в фоновом потоке. Я пытался использовать GCD. Но проблема для меня в том, что я хочу получить некоторые значения из этого процесса после его завершения. Скажите, прежде чем вставить элемент в базу данных, я проверяю, существует ли элемент уже. Пожалуйста, смотрите код ниже

__block Boolean isExisting = false;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
                                             (unsigned long)NULL), ^(void) {
        NSString *path = [SqliteManager initDatabase];
        if(sqlite3_open([path UTF8String], &database) == SQLITE_OK)
        {
            NSString *query = [NSString stringWithFormat:@"select count (campaignId) from POTC where Id='%@' and taskid='%@' and pocId='%@' and userId='%@'",[submission.campaignId  stringRepresentation],[submission.taskId stringRepresentation],[submission.pocId stringRepresentation],[[UUID UUIDWithString:submission.userId] stringRepresentation]];
            const char *sql = [query cStringUsingEncoding:NSASCIIStringEncoding];
            sqlite3_stmt *selectStatement;
            if (sqlite3_prepare_v2(database, sql, -1, &selectStatement, NULL) == SQLITE_OK)
            {
                while (sqlite3_step(selectStatement) == SQLITE_ROW)
                {
                    if (sqlite3_column_int(selectStatement, 0) >0)
                    {
                        isExisting = true;
                        break;
                    }
                }
                sqlite3_finalize(selectStatement);
            }
            sqlite3_close(database);
        }
        return isExisting;
    });

Но приведенный выше код с оператором return не будет работать, поскольку dispatch-async ожидает блок кода void. Как я могу добиться того же в IOS? Есть ли что-то похожее на блок завершения анимации в IOS?

4 ответа

Решение

Блок должен иметь возвращаемый тип void, потому что некуда возвращать значение в асинхронном блоке.

Переменная isExisting квалифицирована __block это означает, что он будет установлен всякий раз, когда блок назначается на него. К сожалению, ваш основной поток не сможет получить к нему доступ после выхода из области. Осторожнее всего сделать так, чтобы ваш блок вызывал другой метод (или функцию, или блок), который устанавливает переменную или свойство, которое, как вы знаете, будет по-прежнему после завершения асинхронного блока.

например, у вас может быть метод в делегате приложения, который будет вызываться по завершении.

// in your appDelegate implementation

-(void) updateUIAfterDatabaseUpdate: (bool) isExisting
{
    if (isExisting)
    {
        // e.g. display an error
    }
    else
    {
        // successful update
    }
}

// The update code

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
                                             (unsigned long)NULL), ^(void) {
        bool isExisting = false;

        NSString *path = [SqliteManager initDatabase];
        if(sqlite3_open([path UTF8String], &database) == SQLITE_OK)
        {
            // Snipped for clarity
        }
        dispatch_async(dispatch_get_main_queue(), ^(void) {
            [appDelegate updateUIAfterDatabaseUpdate: isExisting] ;
        });
    });

Диспетчеризация в главной очереди гарантирует, что метод вызывается в главном потоке, чтобы он мог выполнять обновления пользовательского интерфейса.

Возможно, вам следует создать функцию с блоком завершения.

Я определяю мины, как это:

typedef void (^myBlock)(type1 param1,
                        type2 param2);

-(void)myAsyncFunctionWithParam:(id)paramSend withCompletionBlock:(myBlock)returningBlock
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //here everything you want to do
        // especially defining 2 parameters to return (let them be p1 & p2)
        dispatch_async(dispatch_get_main_queue(), ^{
            returningBlock(p1,p2);
        });
    });
}

и вы можете использовать его как:

[self myAsyncFunctionWithParam:ps 
           withCompletionBlock:^(type1 paramToUse1,
                                 type2 paramToUse2)
           {
               //You can use here paramToUse1 and paramToUse2
           }
];

Вы можете использовать любой тип, который вы хотите для типа в блоке: NSString, NSDate,... (не прощайте *, если это необходимо)

То, как вы думаете, теряет преимущество фонового потока:). Вы должны реструктурировать свою программу, чтобы она лучше соответствовала асинхронной парадигме. Вы запускаете фоновую работу асинхронно, а затем, когда она закончится, вы можете отправить сообщение получателю для выполнения другой работы. В вашем коде, например, вы можете использовать уведомление или просто отправить сообщение. как это:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
                                             (unsigned long)NULL), ^(void) {
        NSString *path = [SqliteManager initDatabase];
        if(sqlite3_open([path UTF8String], &database) == SQLITE_OK)
        {
            NSString *query = [NSString stringWithFormat:@"select count (campaignId) from POTC where Id='%@' and taskid='%@' and pocId='%@' and userId='%@'",[submission.campaignId  stringRepresentation],[submission.taskId stringRepresentation],[submission.pocId stringRepresentation],[[UUID UUIDWithString:submission.userId] stringRepresentation]];
            const char *sql = [query cStringUsingEncoding:NSASCIIStringEncoding];
            sqlite3_stmt *selectStatement;
            if (sqlite3_prepare_v2(database, sql, -1, &selectStatement, NULL) == SQLITE_OK)
            {
                while (sqlite3_step(selectStatement) == SQLITE_ROW)
                {
                    if (sqlite3_column_int(selectStatement, 0) >0)
                    {

                      // Here you can send the notification with the data you want.
                        break;
                    }
                }
                sqlite3_finalize(selectStatement);
            }
            sqlite3_close(database);
        }
        return isExisting;
    });

Вам не нужно возвращать что-то, потому что isExisting станет true, и если вы получите доступ к его значению после завершения выполнения блока, он вернет true.

Другие вопросы по тегам