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);
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);
}
});
});