How to read NSArray value in dispatch_async - ios

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
NSLog(#"count: %lu", (unsigned long)[[myClass getFinalPath]count]);
dispatch_async(dispatch_get_main_queue(), ^(void){
//Run UI Updates
});
});
I am trying to read an NSArray value, if I read NSArray value outside the dispatch_async I am able to read it but when it's inside it's returning zero, I am very new to dispatch_async and I'm having problem understanding the tutorials and reference from Apple I have read, please if anybody could help. my [myClass getFinalPath] returns an NSArray filled with certain amount of value btw.

dispatch_async captures the values of the variables in the current scope and executes the code inside the block "later" asynchronously. This code
__block NSInteger i = 0;
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
i = 10;
NSLog(#"inside %ld", i);
});
NSLog(#"outside %ld", i);
logs always first outside 0, then inside 10
You have to read and process the array inside the dispatch block

pass array as parameter to function
(void)passArrayToDispatchSync:(NSArray *)someArray
{
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
NSLog(#"count: %lu", (unsigned long)[someArray count]);
dispatch_async(dispatch_get_main_queue(), ^(void){
//Run UI Updates
});
});
}

Related

How to execute second dispatch_async when previous first dispatch_async is finished?

I would like to add a dispatch_async sequentially but I would like them not to be launched randomly.
I would like for example :
dispatch_async 1 begins...
dispatch_async 1 ends.
dispatch_async 2 begins...
dispatch_async 2 ends.
dispatch_async 3 begins...
dispatch_async 3 ends.
I need to update a sqlite, and the informations in the first dispatch are necessary for the second dispatch...
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"%#",[connection currentRequest]);
NSLog(#"connectionDidFinishLoading");
NSError* error;
NSString *responseKey = [self getResponseKey:connection];
NSDictionary* response = [NSJSONSerialization JSONObjectWithData:[receivedData objectForKey:responseKey] options:kNilOptions error:&error];
//NSLog(#"%#", response);
if (error)
{
NSLog(#"Error: %#", error);
NSLog(#"Error: Response strange format, not an NSArray and not a NSString!\n%#", [[NSString alloc] initWithData:[receivedData objectForKey:responseKey] encoding:NSUTF8StringEncoding]);
}
NSLog(#"connection url : %#", connection.currentRequest.URL);
if ([[NSString stringWithFormat:#"%#", [connection currentRequest]] rangeOfString:#"getSynchroGuest?"].location != NSNotFound)
{
NSLog(#"response success");
if ([[response valueForKey:#"lignes"] isKindOfClass:[NSArray class]])
{
if ([[response valueForKey:#"lignes"] count] > 0)
{
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
//Background Thread
[self fillDataBaseWithDict:response];
nbTotal = nbTotal + PACKET_FOR_SYNC;
[self WebServiceSynchroGuest:self.activityIndicator withSynchroBtn:synchroBtn withNbTotal:nbTotal];
});
}
}
...
Thanks in advance.
SOLUTION:
dispatch_async(serialDispatchQueue, ^{
[self fillDataBaseWithDict:response];
nbTotal = nbTotal + PACKET_FOR_SYNC;
dispatch_async(dispatch_get_main_queue(), ^(void){
[self WebServiceSynchroGuest:self.activityIndicator withSynchroBtn:synchroBtn withNbTotal:nbTotal];
});
});
define your own serial queue
and add code like this
dispatch_queue_t yourSerialQueue = dispatch_queue_create("com.testcompany.testproduct.testserialqueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(yourSerialQueue, ^{ /* first task */ });
dispatch_async(yourSerialQueue, ^{ /* second task to be executed after first task */ });
serial queue guarantees that the tasks will be exevuted in the order they are submitted and in a serial manner(one at a time)
To take your Q literally, you have to nest the calls:
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^(void)
{
// do some work
…
// finished here
// The next async code
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^(void)
{
// do some work
…
// finished here
// and so on
}
}
But you should really consider to use a custom serial queue or NSOperation et al.
With a serial Q:
dispatch_queue_t stepByStepQueue = dispatch_queue_create("com.you.taks", NULL);
dispatch_async(stepByStepQueue,
^(void)
{
// Step
});
dispatch_async(stepByStepQueue,
^(void)
{
// By
});
dispatch_async(stepByStepQueue,
^(void)
{
// Step
});
You can put all dispatch_async as a Serial Queue and execute one by one
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block1
NSLog(#"Block1");
[ NSThread sleepForTimeInterval:5.0];
NSLog(#"Block1 End");
});
dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block2
NSLog(#"block 2");
[ NSThread sleepForTimeInterval:10.0];
NSLog(#"Block2 End");
});
dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block3
NSLog(#"block 3");
[ NSThread sleepForTimeInterval:15.0];
NSLog(#"Block3 End");
});
dispatch_group_notify(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block4
NSLog(#"block 4");
[ NSThread sleepForTimeInterval:20.0];
NSLog(#"Block4 End");
});
Best approach would be to use NSOperations & add them in queue.so it would be called in sequence of completion
But if you want to do it in same way then define completion blocks & add your dispatch_async2 & dispatch_Async3 in completion blocks & call those completion blocks in end.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// do some long running processing here
// Check that there was not a nil handler passed.
if( completionHandler1 )
{
completionHandler1();
}
});
});
add code like this
You can set PRIORITY as well.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
});
});
What you want is easily achieved with Operation instead of GCD. Operation class has addDependency(_ op: Operation) function. Put your code in BlockOperation instances, add dependencies and run them in OperationQueue

How to get notified when enumerateLinesUsingBlock is completed

Is there any way to notify when a enumerateLinesUsingBlock is completed? Please check below code. I am calling createFastSearchData method with chunk by chunk data in a while loop and inside that taking each lines and processing it. In while condition I am checking the length of the main string and I want to continue untill it completes the total length. So I want to make sure that enumerateLinesUsingBlock is completed before the while loop trigger again.
while(<checking the length of the mainstring>){
[self createFastSearchData:string];
}
- (void)createFastSearchData:(NSString *)newChunk{
[newChunk enumerateLinesUsingBlock:^(NSString * line, BOOL * stop)
{}];
}
Added:
I am working with blocks and finding difficulty to understand the actual flow. Please check the below code. I want to call fetchCSVData method by passing different values in an array filesToBeFetchedWhat. I want to make sure that, fetchCSVData should not overlap. How can I do that? Please help
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
#autoreleasepool {
for (int i = 0; i < filesToBeFetched.count; i++) {
[applicationDelegate fetchCSVData:[filesToBeFetched objectAtIndex:i]];
}
}
dispatch_async( dispatch_get_main_queue(), ^{
NSLog(#"Fetching is done *********************");
});
});
To answer the first part of the question:
All enumerate...UsingBlock methods don't work asynchronously.
Regarding the added part:
Assuming fetchCSVData works also synchronously, that's the preferred way to process the data
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
#autoreleasepool {
for (int i = 0; i < filesToBeFetched.count; i++) {
[applicationDelegate fetchCSVData:[filesToBeFetched objectAtIndex:i]];
dispatch_async( dispatch_get_main_queue(), ^{
NSLog(#"Fetching is done *********************");
});
}
}
});

Call same function from two different threads

I have a function:
- (void)fetchClassListOnCompletion:(void(^) (BOOL success, NSArray *classlist))completionBlock;
I want to call this function from two different thread (may be simultaneously) and want the classlist from the one that finished last.
Need help to implement this
Try this:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[self fetchClassListOnCompletion:^(BOOL success, NSArray *classlist) {
self.classList = classlist;
}];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[self fetchClassListOnCompletion:^(BOOL success, NSArray *classlist) {
self.classList = classlist;
}];
});
self.classList will be assigned to classlist from the block that finishes last.
Because this runs on background thread, make sure your method doesn't need to be run on main thread.

How to create concurrent queues properly with three requests at the same time

I want to get three requests at the same time to improve performance.
Here my current code, it returns only one platform at my tableview:
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.myapp.myqueue", DISPATCH_QUEUE_CONCURRENT);
void(^block_readPS4)() = ^{
self.releasesDict = [NSDictionary dictionaryWithDictionary:[self getDataWithPlatform:#"ps4"]];
};
void(^block_readXONE)() = ^{
self.releasesDict = [NSDictionary dictionaryWithDictionary:[self getDataWithPlatform:#"xboxone"]];
};
void(^block_readPC)() = ^{
self.releasesDict = [NSDictionary dictionaryWithDictionary:[self getDataWithPlatform:#"pc"]];
};
void(^block_write)() = ^{dictionaryWithDictionary:self.releasesDict];
self.releasesArr = [self.releasesDict allKeys];
[self.tableView reloadData];
[self.activityInd stopAnimating];
};
dispatch_async(concurrentQueue,block_readPS4);
dispatch_async(concurrentQueue,block_readXONE);
dispatch_async(concurrentQueue,block_readPC);
dispatch_barrier_async(concurrentQueue, block_write);
I know problem is write in self.releasesDict, how it could be improved?
#ryancrunchi is correct:
You're overwriting self.releasesDict in each read. So in your write
block the value of self.releasesDict will be the last read that the
system performed.
...but his proposed solution doesn't protect the dictionary from concurrent writes, and NSMutableDictionary is not intrinsically thread-safe. You must protect it from concurrent reads + writes and/or writes + writes. Here is one way you might do that (with a detailed walkthrough of everything being done in the comments):
// A local (non-shared) mutable dictionary. Using a local dictionary allows us to know that no one
// else is reading from or writing to it concurrently, which is not guaranteed if we use self.releasesDict
NSMutableDictionary* localDict = [NSMutableDictionary dictionary];
// Your original concurrent queue
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.myapp.myqueue", DISPATCH_QUEUE_CONCURRENT);
// A serial queue to protect localDict from reading and writing.
dispatch_queue_t localDictWriteQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
// Make this queue target the concurrent queue. Not strictly required, but you had everything executing on
// concurrentQueue before, and this preserves that, while also protecting localDict.
dispatch_set_target_queue(localDictWriteQueue, concurrentQueue);
// A dispatch group that allows us to perform an operation only after all the constituent writes have finished.
dispatch_group_t group = dispatch_group_create();
// For each platform, enter the group, then fire off the concurrent block
dispatch_group_enter(group);
dispatch_async(concurrentQueue, ^{
// Fetch into a dictionary that is local to the block.
NSDictionary* x = [self getDataWithPlatform:#"ps4"];
// Then, on the serial localDictWriteQueue merge those entries into the shared local dictionary
dispatch_async(localDictWriteQueue, ^{
[localDict addEntriesFromDictionary: x];
// This balances out the dispatch_group_enter operation we did right before we enqueued this
dispatch_group_leave(group);
});
});
// Second verse, same as the first
dispatch_group_enter(group);
dispatch_async(concurrentQueue, ^{
NSDictionary* x = [self getDataWithPlatform:#"xboxone"];
dispatch_async(localDictWriteQueue, ^{
[localDict addEntriesFromDictionary: x];
dispatch_group_leave(group);
});
});
// Third verse, same as the first
dispatch_group_enter(group);
dispatch_async(concurrentQueue, ^{
NSDictionary* x = [self getDataWithPlatform:#"pc"];
dispatch_async(localDictWriteQueue, ^{
[localDict addEntriesFromDictionary: x];
dispatch_group_leave(group);
});
});
// Then set up the block we want to run at the end... use main queue because it updates UI.
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// Update self.releasesDict all at once, on the main thread
// Note: we do not need to do this read of localDict on localDictWriteQueue because if this block is executing, we know (semantically) that all possible
// write operations have already completed, and that no other writes to localDict are possible because it's local to this method call.
self.releasesDict = localDict;
// Same idea
self.releasesArr = [self.releasesDict allKeys];
// Update UI based on state changes to self.
[self.tableView reloadData];
[self.activityInd stopAnimating];
});
You're overwriting self.releasesDict in each read. So in your write block the value of self.releasesDict will be the last read that the system performed. If you want all read in same NSDictionary declare self.releasesDict as a NSMutableDictionary and init it with :
self.releasesDict = [NSMutableDictionary dictionary];
and in your reads :
[self.releasesDict addEntriesFromDictionary:[NSDictionary dictionaryWithDictionary:/*what you want*/]];

Return method only when ready?

I have a method in which I run a couple of other methods. These have completion blocks, I only want to return a value at the end of my main method once I have a result from each of my sub methods. Example:
-(NSMutableDictionary *)mainMethod
{
[self subMethod1Complete:^(NSMutableArray *results)
{
}
[self subMethod2Complete:^(NSMutableArray *results)
{
}
//return...
}
I only want to return my dictionary at the end once the two sub method have completed. How can I do this?
I did have the idea of storing a BOOL for each method, so I know, NO incomplete and YES complete. So when both are YES, I return my dict. But how I can call it on time and not prematurely?
Update
I have tweaked my code to use a completion block, so when I finally receive the data from two other completion blocks from other methods, I run the final one with compiled results. Below you can see my method. You can see my method below, no success thus far, the final completion block is still getting called prematurely.
Important bits for me. getTitles and getThumbnails methods. In the completion block of these I get the data I need. Only when I have both of these, do I want to call my final completion block of this main method. As a result, it will pass on both titles and thumbnails once they have been received.
-(void)getFeedForUserID:(NSString *)channelID delegate:(id<YTHelperDelegate>)delegate complete:(void (^)(NSMutableDictionary * result))completionBlock properties:(NSString *)element, ...
{
va_list args;
va_start(args, element);
NSMutableArray *array = [NSMutableArray new];
for (NSString *arg = element; arg != nil; arg = va_arg(args, NSString *)) [array addObject:arg];
va_end(args);
NSMutableDictionary *resultsDict = [NSMutableDictionary new];
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_t group = dispatch_group_create();
for (NSString *string in array)
{
if ([string isEqualToString:kFeedElementTitle])
{
dispatch_group_async(group, queue, ^{
[self getTitlesArrayForChannel:channelID completionHandler:^(NSMutableArray *results) {
dispatch_group_async(group, dispatch_get_main_queue(), ^{
[resultsDict setObject:results forKey:kFeedElementTitle];
});
}];
});
}
if ([string isEqualToString:kFeedElementTitle])
{
dispatch_group_async(group, queue, ^{
[self getThumbnailsArrayForChannel:channelID completionHandler:^(NSMutableArray *results) {
dispatch_group_async(group, dispatch_get_main_queue(), ^{
[resultsDict setObject:results forKey:kFeedElementThumbnail];
});
}];
});
}
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
completionBlock(resultsDict);
});
}
You can use GCD and the dispatch groups feature. Here's an article that explains it: http://www.objc.io/issue-2/low-level-concurrency-apis.html#groups
For example in your case, your code might look something like this (shamelessly copied from the article and adapted a bit)...
- (void)asyncMethod {
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_async(group, queue, ^(){
NSMutableArray * results = [self subMethod1];
dispatch_group_async(group, dispatch_get_main_queue(), ^(){
self.subMethod1Results = results;
});
});
dispatch_group_async(group, queue, ^(){
NSMutableArray * results = [self subMethod2];
dispatch_group_async(group, dispatch_get_main_queue(), ^(){
self.subMethod2Results = results;
});
});
// This block will run once everything above is done:
dispatch_group_notify(group, dispatch_get_main_queue(), ^(){
// notify the app that both sets of data are ready
[self notifyWorkIsDone];
// and release the dispatch group
dispatch_release(group);
});
}
This requires a little modification to how your class works, because the above method is asynchronous (which is a good thing--it's not going to block your app while all that work is being done). All you need is some sort of handler to call and notify your app that your data is ready and you can update your UI or do whatever additional processing is necessary.
You are looking for GCD's dispatch_group APIs. Here is some sample code from Apple's Concurrency Programming Guide:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
// Add a task to the group
dispatch_group_async(group, queue, ^{
// Some asynchronous work
});
// 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);
// Release the group when it is no longer needed.
dispatch_release(group);
Comments on updated code:
Are you sure your mistake is not that your second if statement checks kFeedElementTitle a second time instead of kFeedElementThumbnail which I think may be what you intended?
Updated with working example:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSString *kFeedElementTitle = #"some";
NSString *kFeedElementThumbnail = #"strings";
NSArray *array = #[#"some", #"test", #"strings"];
NSMutableDictionary *resultsDict = [NSMutableDictionary new];
NSLog(#"App launched");
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
for (NSString *string in array)
{
if ([string isEqualToString:kFeedElementTitle])
{
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:5]; // simulate network call
dispatch_group_async(group, dispatch_get_main_queue(), ^{
[resultsDict setObject:#"title result" forKey:kFeedElementTitle];
NSLog(#"Received title result");
});
});
}
if ([string isEqualToString:kFeedElementThumbnail]) // Note: this was changed to kFeedElementThumbnail from kFeedElementTitle
{
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:10]; // simulate network call
dispatch_group_async(group, dispatch_get_main_queue(), ^{
[resultsDict setObject:#"thumbnail result" forKey:kFeedElementThumbnail];
NSLog(#"Received thumbnail result");
});
});
}
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(#"final dictionary: %#", resultsDict);
});
return YES;
}
Output:
2013-07-16 21:02:46.468 d[947:a0b] App launched
2013-07-16 21:02:51.471 d[947:a0b] Received title result
2013-07-16 21:02:56.471 d[947:a0b] Received thumbnail result
2013-07-16 21:02:56.472 d[947:a0b] final dictionary: {
some = "title result";
strings = "thumbnail result";
}
you do not know when the blocks are going to return so you will not know if you have the data at the time, if i may make a suggestion you call a method with in those blocks that method will check to see if both dictionaries are set and if they are then continue with the process otherwise don't continue
- (void)mainMethod
{
[self subMethod1Complete:^(NSMutableArray *results)
{
self.result1 = results;
[self method3];
}
[self subMethod2Complete:^(NSMutableArray *results)
{
self.results2 = results;
[self method3];
}
}
- (void)method3 {
if ( self.results1 != nil && self.results2 != nil ) {
[self startProcedure];
} else {
// do nothing
}
}
although all together i would suggest reworking your code to do this differently, simply because you can't guarantee that one of the blocks will be done by the time of the return, let alone both of them
you can also do something like this
-(NSMutableDictionary *)mainMethod
{
[self subMethod1Complete:^(NSMutableArray *results)
{
}
[self subMethod2Complete:^(NSMutableArray *results)
{
}
while(result == nil)
sleep(1);
//return...
}
which again is really bad.... it's just better to re-write the code

Resources