How to return data from background thread ? - ios

I have a method that should return an array which is populated in a background thread. I would like to wait with the return statement until the array is completely populated. How would I do that ? I know how to process data with no return type, but I would like to call this function and get the populated array.
Example (Here the array ends up empty because it returns before array is populated - I would like to do something to return the populated array) :
-(NSArray*)fetchSomeArray{
__block NSArray *arrayToReturn;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,(unsigned long)NULL), ^(void) {
NSArray *someArray = ...; // getting some array from database
arrayToReturn = [NSMutableArray new];
for(int i=0;i<someArray.count;i++){
[arrayToReturn addObject:...];
}
});
return arrayToReturn;
}

Use delegation, or blocks and take out the return arrayToReturn;. After your "for" and inside the "dispatch_async":
dispatch_async(dispatch_get_main_queue(),^{
[myDelegate passComputedArray:arrayToReturn];
});

- (void)someMethod:(void (^)(BOOL result))completionHandler {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//all your functionality
// Check that there was not a nil handler passed.
if( completionHandler ){
//your function to so that you can return
}
});
});
Pass the completionHandler and once done, do your function

If you want to wait until it returns, it is unnecessary to background it.
-(NSArray*)fetchSomeArray{
NSArray *someArray = ...; // getting some array from database
return someArray;
}
But I'll bet that you really want to do something with that returned value which takes some time to compute without blocking the main thread, so you can instead pass the array back to a method in the same object or elsewhere.
-(void)fetchSomeArray{
__block id bself = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,(unsigned long)NULL), ^(void) {
NSArray *someArray = ...; // getting some array from database
[bself arrayFetched:someArray];
});
return arrayToReturn;
}
-(void)arrayFetched:(NSArray*)array{
// do something with the returned array
}

The easiest way to deal with this would be to call a function once the array is complete and process the array there.
-(void)fetchSomeArray{
__block NSArray *arrayToReturn;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,(unsigned long)NULL), ^(void) {
NSArray *someArray = ...; // getting some array from database
arrayToReturn = [NSMutableArray new];
for(int i=0;i<someArray.count;i++){
[arrayToReturn addObject:...];
}
[self finishedArrayResult:arrayToReturn];
});
}
But if you wanted that return function to update anything n the UI you would need to run that function in the main thread and not the background. To do that you could either use performSelector: onThread: withObject: waitUntilDone: or you could use another dispatch_async using the main thread instead of global thread.

Related

Core Data Performing Fetch Request with Completion Handler or execute in some other thread other than Main thread

In my iOS application I am using Core Data.
For table View listing I use NSFetchedResultsController and
Connecting to Remote store I use NSIncrementalStore.
My FetchedResultsController Context is having MainQueue Cuncurrency type.(I couldn't do it with a PrivateQueueCurrencyTYpe).
For resolving Fault, for a many relationship, the executeFetchResultsCall:withContext:error method is executed from my IncrementalStore subclass.
Inside the executeFetchResults method, I will invoke the API (connecting to remote server) if it is not available in my local database.
myarray = [object representationsForRelationship:#"manyconnection" withParams:nil];
Now I need the results array in return synchronously to be returned to the ExecuteFetchResultsMethod. Also this operation should be executed on Main thread.
So I am having only one option to fetch the results from server which causes the UI to unresponsive for the specified sleep time.
-(RequestResult*)makeSyncJsonRequest{
__block RequestResult *retResult = [[RequestResult alloc] init];
__block BOOL block = YES;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
void (^resultBlock)(RequestResult*) = ^(RequestResult* result){
if(!retResult.error)
retResult = result;
block = NO;
dispatch_group_leave(group);
};
// Add a task to the group
dispatch_group_async(group, queue, ^{
// Some asynchronous work
dispatch_group_enter(group);
[self makeAsyncJsonRequestWithBlock:resultBlock];
});
// Do some other work while the tasks execute.
// When you cannot make any more forward progress,
// wait on the group to block the current thread.
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
return retResult;
}
As the above operation is being executed on main thread,the UI hangs.
Inorder to make the UI smoother, I need to carry out the executeFetchrequest in some other thread which is not possible.
It also expects the results array in return.
Is there any option to carry out this something like in a completion handler manner?
or
Any alternate methods or design to work this proper.
Any Help is highly appreciated.
This is a skeleton, using a dispatch_group, assuming you are using an NSFetchedResultsController to update your UITableView:
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// do your setup (FetchedResultsController and such)
[self syncData];
}
- (void)syncData
{
NSArray<Entity*> *results = [self fetchData];
BOOL needsUpdateFromServer = YES; // check your results and set this bool accordingly
if (!needsUpdateFromServer) {
return;
}
__block ServerResponse *fromServer = nil;
__block dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[self loadDataFromServer:^(ServerResponse *response) {
fromServer = response;
dispatch_group_leave(group);
}];
dispatch_group_notify(group,dispatch_get_main_queue(),^{
[self persistData:fromServer];
/*
According to our discussion, you are using an NSFetchedResultsController.
So your tableView should update automatically after persisting the data.
*/
});
}
- (void)loadDataFromServer:(void (^)(ServerResponse *response))completion
{
// [someDownloadService downloadDataFromServerInBackgroundWithCompletion:^(ServerResponse* response){
dispatch_async(dispatch_get_main_queue(), ^{
completion(response);
});
// }];
}
- (NSArray<Entity*>*)fetchData
{
NSArray<Entity*> *results = nil;
// fetch from core data and return it
return results;
}
- (void)persistData:(NSArray<ServerResponse*> *)serverResponses
{
// parse whatever you get from server
// ... and persist it using Core Data
}
#end

cannot execute method insde dispatch_async()

I am facing something I cannot sort out. I am downloading json data and instantiating Core Data objects with the returned value (inside a dispatch_async(get_main_queue)). I try to present an other view (with tableView containing these objects) after everything is completed but my tableviewcontrollers are not called. However everything works fine if I present my viewController from a method outside this block (which is inside connectionDidFinishing NSURLConnection).
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
id jsonObject = [NSJSONSerialization JSONObjectWithData:_downloadedData options:NSJSONReadingAllowFragments error:&error];
if ([jsonObject isKindOfClass:[NSArray class]]) {
NSArray *deserializedArray = (NSArray *)jsonObject;
if (deserializedArray.count > 0) {
dispatch_async(dispatch_get_main_queue(), ^{
for (NSDictionary *jsonElement in deserializedArray)
{
// Create a new location object and set its props to JsonElement properties
Patient *syncPatient = [[PatientStore sharedStore] createPatient];
syncPatient.firstName = jsonElement[#"firstname"];
syncPatient.lastName = jsonElement[#"lastname"];
}
});
[self login]; // --> does not work.
}
}
}
[self login] does not work. But it works if I call it from outside the didFinishLoading method.
It has to do I think with the dispatch_async() but I don't know how to call this method automatically from outside (for example with a "I have finish" notification).
Does anyone could help?
Thanks a lot!
UPDATE
It does not work either inside the block. It looks like [self login] happened before the for loop instructions.
if (deserializedArray.count > 0) {
dispatch_async(dispatch_get_main_queue(), ^{
for (NSDictionary *jsonElement in deserializedArray)
{
// Create a new location object and set its props to JsonElement properties
Patient *syncPatient = [[PatientStore sharedStore] createPatient];
syncPatient.firstName = jsonElement[#"firstname"];
syncPatient.lastName = jsonElement[#"lastname"];
}
[self login]; // --> does not work either here. As if it was executed before what is inside the for loop.
});
}
}
}
[self login] is executed immediately after the core data save starts because it is outside the block. If you put it inside, it will execute after the loop finishes.
You can test this with a simple command line project, which this content in main.swift:
import Foundation
dispatch_async(dispatch_get_main_queue()) {
var j = 0
for _ in 1...1000 { j++ }
println("The value of j is \(j).")
}
dispatch_main()
Output: The value of j is 1000.
The println is clearly executed after the loop.
After creating your objects, do not forget to save:
[managedObjectContext save:nil];

Asynchronous Recursive Calls using Box sdk - How to iterate all files in using iOS BOX sdk api

I am trying to iterate all images stored in a Box account.
The following code isn't correct and can't seem to find the error.
The problem seems to be waiting for all of the asynchronous recursive calls to complete in order to know when there are no more images to be fetched in order to signal completion
-(void)enumerateFiles
{
[self recursiveEnumerateFilesAtPath:#"0" completion:nil];
}
-(void)recursiveEnumerateFilesAtPath:(NSString *)folderID completion:(void(^)())block
{
static NSArray *imageExtensions;
imageExtensions = #[#"jpg",#"jpeg",#"png"];
[self enumerateFilesAtPath:folderID completion:^(BoxCollection *collection) {
NSUInteger numberOfItems = collection.numberOfEntries;
for (int i = 0; i < numberOfItems; i++) {
id model = [collection modelAtIndex:i];
if ([model isKindOfClass:[BoxFolder class]])
{
BoxFolder *folder = (BoxFolder *)model;
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[self recursiveEnumerateFilesAtPath:folder.modelID completion:^{
dispatch_group_leave(group);
}];
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
} else if ([model isKindOfClass:[BoxItem class]])
{
BoxItem *item = (BoxItem *)model;
NSString *extension = [[item.name pathExtension] lowercaseString];
if ([imageExtensions containsObject:extension])
{
[self.items addObject:model];
}
}
}
if (block)
{
block();
}
}];
}
-(void)enumerateFilesAtPath:(NSString *)folderID completion:(void(^)(BoxCollection *collection))block
{
BoxCollectionBlock success = ^(BoxCollection *collection)
{
block(collection);
};
BoxAPIJSONFailureBlock failure = ^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, NSDictionary *JSONDictionary)
{
block(nil);
};
[[BoxSDK sharedSDK].foldersManager folderItemsWithID:folderID requestBuilder:nil success:success failure:failure];
}
The key to a viable approach is to make the inner loop, that is
for (int i = 0; i < numberOfItems; i++) {..}
a construct which sequentially invokes a number of asynchronous tasks and is itself asynchronous. The asynchronous task should be a block (^) which will invoke the outer method and will capture state so that its outer method recursiveEnumerateFilesAtPath: can be invoked in a recursive manner.
Note that this is not a recursion we know from functions, where a function calls itself before it returns, and where the state is hold on the program stack. Here, we rather have an iteration (asynchronous loop) - and there's no state on the stack, instead the state required for the "recursion" is hold in the variables captured in the blocks, and these reside on the heap.
That is, you need to transform the "for loop" (for (int i = 0; i < numberOfItems; i++) {..}) into some asynchronous method, e.g:
- (void) forEach:(NSArray*)objects applyTask:(task_t) completion:(completion_t)completionHandler;
where task is asynchronous, has a completion handler, and (conditionally) calls the outer method recursiveEnumerateFilesAtPath:.
This forEach construct could be implemented using a NSOperationQueue, whose max concurrent operations is set to one, and where the task is a NSOperation with a completion handler. Alternatively - and simpler in this case, it can be implemented using dispatch queues and a dispatch group. Or - even more simpler, it can be implemented using an "asynchronous loop". I'll show a possible implementation, see "NSArray Category forEachApplyTask:completion: below.
Now, suppose there is a Category for NSArray with an asynchronous forEachApplyTask:completion: method:
#interface NSArray (AsyncExtension)
- (void) forEachApplyTask:(unary_async_t)task completion:(completion_t) completion;
#end
This method takes a block of type unary_async_t as first parameter:
typedef void (^unary_async_t)(id input, completion_t completion);
and a completion handler as second parameter:
typedef void (^completion_t)(id result);
This typedef of a completion handler, is generic and can be used for all completion handler variants.
Let's suppose, the block task will be sequentially applied for each element in the receiver (the array). And suppose, this array is an array of "model"s in your original code -- you just need to create an array of "model"s out from the BoxCollection object.
When finished, the completion handler passes a result, which is an array containing the result of each task in the same order.
This task executes the block ({..}) in your former for loop.
Your method recursiveEnumerateFilesAtPath: will become inevitable asynchronous (since to invokes an asynchronous method in its implementation), and thus gets a completion handler parameter as well.
With the given assumptions made above, you can implement your problem as shown below:
-(void)recursiveEnumerateFilesAtPath:(NSString *)folderID
completion:(completion_t)finalCompletionHandler
{
[self enumerateFilesAtPath:folderID completion:^(BoxCollection *collection) {
NSArray* models = ...; // create an NSArray from the collection
[models forEachApplyTask:^(id model, completion_t taskCompletionHandler) {
if ([model isKindOfClass:[BoxFolder class]]) {
BoxFolder *folder = (BoxFolder *)model;
[self recursiveEnumerateFilesAtPath:folder.modelID completion:^(id result){
// result should be #"finished folder"
taskCompletionHandler(#"folder"); // taskCompletionHandler will never be nil
}];
}
else if ([model isKindOfClass:[BoxItem class]]) {
BoxItem *item = (BoxItem *)model;
NSString *extension = [[item.name pathExtension] lowercaseString];
if ([imageExtensions containsObject:extension]) {
[self.items addObject:model];
}
taskCompletionHandler(#"item");
}
}
completion:^(id array){
// this folder is finished. array may be for example #[#"folder", #"item", #"item"]
if (finalCompletionHandler) {
finalCompletionHandler(#"finished folder");
}
}];
}];
}
Caution: not tested, you may experience issues!
Note, that everything runs asynchronous, and that there is no recursion whose state is put on the stack. The required state for recursively iterating the folders is put on the heap, enclosed in captured variables in the block.
The NSArray Category method forEachApplyTask:completion: is a reusable component, which can be utilized in many cases. One can implement it as follows:
Implementation of Category NSArray
/**
Objective:
Asynchronously transform or process an array of items - one after the
other and return the result of each transform in an array.
Synopsis:
void transform_each(NSArray* inArray, unary_async_t task, completion_t completion);
*/
#import <Foundation/Foundation.h>
#import <dispatch/dispatch.h>
/**
Typedef of a generic completion handler for an asynchronous task.
The parameter _result_ is the eventual result of the asynchronous
task. In case the task has been failed _result_ SHALL be an
NSError object.
*/
typedef void (^completion_t)(id result);
/**
Typedef for an asynchronous "transform" function. It's an
unary block taking an input as parameter and signals
the eventual result via a completion handler.
*/
typedef void (^unary_async_t)(id input, completion_t completion);
/**
`transform_each` sequentially applies an asynchronous transform function
to each object in the input array _inArray_ and signals the result as an
array containing the result of each transform applied to the input object.
Function `transform_each` is itself a asynchronous function, that is,
its eventual result will be signaled to the client through a completion
handler.
The result array contains the transformed objects in order of the
corresponding input array.
*/
void transform_each(NSArray* inArray, unary_async_t task, completion_t completion);
// implementation
static void do_each(NSEnumerator* iter, unary_async_t task, NSMutableArray* outArray, completion_t completion)
{
id obj = [iter nextObject];
if (obj == nil) {
if (completion)
completion([outArray copy]);
return;
}
task(obj, ^(id result){
[outArray addObject:result];
do_each(iter, task, outArray, completion);
});
}
void transform_each(NSArray* inArray, unary_async_t task, completion_t completion) {
NSMutableArray* outArray = [[NSMutableArray alloc] initWithCapacity:[inArray count]];
NSEnumerator* iter = [inArray objectEnumerator];
do_each(iter, task, outArray, completion);
}
/*******************************************************************************
Example
*******************************************************************************/
// A Category for NSArray
#interface NSArray (AsyncExtension)
- (void) async_forEachApplyTask:(unary_async_t) task completion:(completion_t) completion;
#end
#implementation NSArray (AsyncExtension)
- (void) async_forEachApplyTask:(unary_async_t) task completion:(completion_t) completion {
transform_each(self, task, completion);
}
#end
See also: https://gist.github.com/couchdeveloper/6155227

Download and Parse Data in Background Thread

I have a UITableView that gets its data from an array. Populating that array, however, requires downloading and parsing large chucks of data from the Web. That being the case, I'd like perform those operations in a background thread. Here's what I've got so far:
#interface MyClass()
#property (nonatomic, strong) NSArray *model;
#end
#implementation MyClass
- (void) getData {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:SOME_URL]];
if (data) {
NSMutableArray *arr = [NSMutableArray array];
//Populate arr with data just fetched, which can take a while
dispatch_async(dispatch_get_main_queue(), ^{
//THIS IS THE STEP I AM UNSURE ABOUT. SHOULD I DO:
self.model = arr;
//OR
self.model = [NSArray arrayWithArray:arr];
//OR
self.model = [arr copy];
//OR
//something else?
});
}
});
}
#end
Thank you!
// you can use any string instead "mythread"
dispatch_queue_t backgroundQueue = dispatch_queue_create("com.mycompany.myqueue", 0);
dispatch_async(backgroundQueue, ^{
// Send Request to server for Data
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:SOME_URL]];
dispatch_async(dispatch_get_main_queue(), ^{
// Receive Result here for your request and perform UI Updation Task Here
if ([data length] > 0) {
// if you receive any data in Response, Parse it either (XML or JSON) and reload tableview new data
}
});
});
You should do:
self.model = arr;
The reference to self calls the setter, which will release any previous references in that variable and then add a reference count to arr so it doesn't go out of scope. If you were accessing the ivar directly you would do:
[model release];
model = [arr retain];
Take a look at this link Understanding dispatch_async and this https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html
You should add DISPATCH_QUEUE_PRIORITY_BACKGROUND instead of DISPATCH_QUEUE_PRIORITY_DEFAULT to run this on the background.
By using DISPATCH_QUEUE_PRIORITY_DEFAULT you just made your task be classified as a normal task. If you have changed it to higher or lower priority, the queue would have run it before or after some other tasks, respectively.

Why I get error EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) when I use GCD

Could somebody, please, explain me why I get error EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) in dispatch_semaphore_wait in the following code:
-(void) initialize {
dispatch_queue_t queue = dispatch_queue_create("My queue", NULL);
dispatch_semaphore_t sem = dispatch_semaphore_create(1);
self.queue = queue;
self.sem = sem;
self.myarray = [[NSMutableArray alloc]init];
[self.myarray addObject: [[MyObject alloc] init]];
}
-(MyObject *) method1 {
//do something
dispatch_semaphore_wait(self.sem, DISPATCH_TIME_FOREVER);
MyObject *obj = [self.myarray objectAtIndex:0];
dispatch_barrier_sync(self.queue, ^{
[self.myarray removeObjectAtIndex:0];
});
return obj;
}
-(void) method2:(MyObject *)object {
//do something
dispatch_barrier_async(self.queue, ^{
[self.myarray addObject:object];
dispatch_semaphore_signal(self.sem);
});
}
I found similar question Why does this code cause "EXC_BAD_INSTRUCTION"?, but in my case I am using ARC and I do not write explicitly nowhere dispatch_release(sem);
The sem you created in your initialize method is locally scoped to that method. It needs to be accessible to the other methods. If you have an iVar named sem that you're attempting to assign, you're shadowing it by declaring a local variable in initialize. (Same thing with queue, by the way.)
Also, you appear to have a typo here, in that you call dispatch_semaphore_wait(sen, DISPATCH_TIME_FOREVER); (i.e. se n vs se m)
You are allowing simultaneous access to the array self.myarray without sufficient protection. You modify the array with -addObject: and -removeObjectAtIndex: on the serial queue self.queue but you read from it using -objectAtIndex: without any protection. That means you may be reading from it at the same time you're writing to it, which is not safe. You need to also put the -objectAtIndex: call on the serial queue.
Also, you are using barrier functions with a serial queue, which doesn't make any sense.
-(MyObject *) method1 {
//do something
dispatch_semaphore_wait(self.sem, DISPATCH_TIME_FOREVER);
__block MyObject *obj;
dispatch_sync(self.queue, ^{
obj = [self.myarray objectAtIndex:0];
[self.myarray removeObjectAtIndex:0];
});
return obj;
}
-(void) method2:(MyObject *)object {
//do something
dispatch_async(self.queue, ^{
[self.myarray addObject:object];
dispatch_semaphore_signal(self.sem);
});
}
This kind of crash will happen when you are running a (vector)extension which is not supported on your CPU.
For example, in xcode 5 under "project-settings / build-settings / Code Generation, set the
"Enable Additional Vector extensions" to "AVX2". Build your executable.
Now run it on an:
Intel Core i5: it's going to crash (wherever the compiler decided to use avx2) with 'exc_i386_invop subcode=0x0'.
Intel Core i7: it will work.

Resources