Search code examples
objective-cmultithreadingasynchronousblock

Asynchronous Block Return Issue


Using a function here to return a value from the asynchronous block (just a database query). Problem is the application freezes and terminates due to a memory issue. Seeking advice whether it would be better to run this on the main thread or should I avoid that? Note it is being executed on another thread.

- (NSString *)databaseQuery:(NSString*)ingredient {
    __block NSString *valueType = nil;
    __block BOOL done = NO;
    [[[_ref child:@"ingredients"] queryEqualToValue:valueType childKey:ingredient] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
        for (FIRDataSnapshot *child in snapshot.children) {
            valueType = child.value;
        }
        done = YES;
    } withCancelBlock:^(NSError * _Nonnull error) {
        NSLog(@"%@", error.localizedDescription);
        done = YES;
    }];

    while (!done) {
        [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
    }
    return valueType;
}

UPDATE 1: Attempted using the code below and it produces the same outcome.

- (NSString *)databaseQuery:(NSString*)ingredient {
    __block NSString *valueType = nil;
    dispatch_semaphore_t sem = dispatch_semaphore_create(0);
    FIRDatabaseQuery *query = [[_ref child:@"ingredients"] queryEqualToValue:valueType childKey:ingredient] ;
    [query observeEventType:FIRDataEventTypeChildAdded
                  withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
                      valueType = snapshot.value;
                      dispatch_semaphore_signal(sem);
                  }
            withCancelBlock:^(NSError * _Nonnull error) {
                NSLog(@"%@", error.localizedDescription);
                dispatch_semaphore_signal(sem);
            }];

    dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
    return valueType;
}

UPDATE 2:

Changed the format so the function is not returning from a block. It only returns the FIRDatabaseQuery.

- (FIRDatabaseQuery *)databaseQuery:(NSString*)ingredient {
    __block NSString *valueType = nil;
    FIRDatabaseQuery *query = [[_ref child:@"ingredients"] queryEqualToValue:valueType childKey:ingredient];
    return query;
}

The part below is in another procedure. Except the value returned is null.

query = [self databaseQuery:substring];
            [query observeEventType:FIRDataEventTypeChildAdded
                          withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
                              idValue = snapshot.value;
                          }
                    withCancelBlock:^(NSError * _Nonnull error) {
                        NSLog(@"%@", error.localizedDescription);
                    }];
            NSLog(@"%@", idValue);

Solution

  • Solved: As much as I would want to avoid NSRunLoop, the problem was that I had one already running and I hadn't stopped it.

    Answer was to add CFRunLoopStop(CFRunLoopGetCurrent()); to the application. Should replace this using the semaphores.

    Problem is, this is in a loop. After the first items searched, it get's stuck and hangs again. To Solve this, I've used dispatch groups as suggested.

    Note: Declare the dispatch group before the loop as well as the idValue.

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
                    dispatch_group_enter(_groupSearch);
                    dispatch_async(queue, ^{
                    [[self databaseQuery:searchItem] observeEventType:FIRDataEventTypeChildAdded
                                  withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
                                      idValue = snapshot.value;
                                      dispatch_group_leave(_groupSearch);
                                  }
                            withCancelBlock:^(NSError * _Nonnull error) {
                                NSLog(@"%@", error.localizedDescription);
                                dispatch_group_leave(_groupSearch);
                                }];
                    });
                    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                        dispatch_group_wait(_groupSearch, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)));
                        dispatch_sync(queue, ^{
                            if (idValue != NULL) {
                                NSLog(@"%@",idValue);
                            }
                        });
                    });