Currently I am working on an app to capture images of different exposurePointOfInterest. Basically the steps to that are:
Set focus on point A
Capture
Set focus on point B
Capture
I had to put a redundant for loop between step 1 & 2 and step 3 & 4 to allow some time for the lens to actually focus on the intended points, otherwise both captures at step 2 & 4 would result in the same picture. This works perfectly. But, I believe this is not the best way to solve this problem.
I have tried putting this code instead of the for loop:
[self performSelector:#selector(captureStillImage) withObject:#"Grand Central Dispatch" afterDelay:1.0]
But when I ran it, it ran as if the selector captureStillImage is never executed. Is there anything that I did wrong? Or is there a better solution that anyone can advise me?
The function I call to capture multiple images looks like this:
-(void)captureMultipleImg
{
//CAPTURE FIRST IMAGE WITH EXPOSURE POINT(0,0)
[self continuousExposeAtPoint:CGPointMake(0.0f, 0.0f)];
NSLog(#"Looping..");
for(int i=0; i<100000000;i++){
}
NSLog(#"Finish Looping");
[self captureStillImage];
//CAPTURE FIRST IMAGE WITH EXPOSURE POINT(0,0)
[self continuousExposeAtPoint:CGPointMake(0.5f, 0.5f)];
NSLog(#"Looping..");
for(int i=0; i<100000000;i++){
}
NSLog(#"Finish Looping");
[self captureStillImage];
}
And the code for captureStillImage looks like this:
-(void)captureStillImage
{
AVCaptureConnection *connection = [stillImage connectionWithMediaType:AVMediaTypeVideo];
typedef void(^MyBufBlock)(CMSampleBufferRef, NSError*);
MyBufBlock h = ^(CMSampleBufferRef buf, NSError *err){
NSData *data = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:buf];
[self setToSaveImage:[UIImage imageWithData:data]];
NSLog(#"Saving to Camera Roll..");
//Saving photo to camera roll
UIImageWriteToSavedPhotosAlbum(toSaveImage, self, #selector(image:didFinishSavingWithError:contextInfo:), nil);
toSaveImage = NULL;
};
[stillImage captureStillImageAsynchronouslyFromConnection:connection completionHandler:h];
}
The code for continuousExposeAtPoint: function:
-(void)continuousExposeAtPoint:(CGPoint)point
{
if([device isExposurePointOfInterestSupported] && [device isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]){
if([device lockForConfiguration:NULL]){
[device setExposurePointOfInterest:point];
[device setExposureMode:AVCaptureExposureModeContinuousAutoExposure];
[device unlockForConfiguration];
NSLog(#"Exposure point of intereset has been set to (%f,%f)",point.x, point.y);
}
}
}
Thanks in advance!
Instead of the dummy loop you can use performSelector:withObject:afterDelay:
I'm going out of a limb here, since I would like to suggest a different approach which completely avoids "busy waiting" or "run loop waiting".
If I understood the camera correctly, it may take a certain duration until after the exposure point has been set by the camera. There is the property adjustingFocus which reflects this state of the camera. This property is KVO compliant and we can use KVO to observe its value.
So, the idea is to set the exposure point, and then observe the property adjustingFocus. When it's value changes to NO, the camera is finished setting the exposure point.
Now, we can leverage KVO to call a completion hander immediately after the setting is complete. Your method to setup the exposure point becomes asynchronous with a completion handler:
typedef void (^completion_t) ();
-(void)continuousExposeAtPoint:(CGPoint)point
completion:(completion_t)completionHandler;
Assuming you have properly implemented KVO in the method above you can use it as follows:
-(void)captureMultipleImg
{
[self continuousExposeAtPoint:CGPointMake(0.0f, 0.0f) completion:^{
[self captureStillImage];
[self continuousExposeAtPoint:CGPointMake(0.5f, 0.5f) completion:^{
[self captureStillImage];
}];
}];
}
Edit:
Now, method captureMultipleImg became asynchronous as well.
Note:
A method invoking an asynchronous method becomes itself asynchronous.
Thus, in order to let the call-site know when its underlying asynchronous task is finished, we may provide a completion handler:
typedef void (^completion_t)();
-(void)captureMultipleImagesWithCompletion:(completion_t)completionHandler
{
[self continuousExposeAtPoint:CGPointMake(0.0f, 0.0f) completion:^{
[self captureStillImage];
[self continuousExposeAtPoint:CGPointMake(0.5f, 0.5f) completion:^{
[self captureStillImage];
if (completionHandler) {
completionHandler();
}
}];
}];
}
A button action may be implemented as follows:
- (void)captureImages {
[self showLabel];
self.captureImagesButton.enabled = NO;
[manager captureMultipleImagesWithCompletion:^{
dispatch_async(dispatch_get_main_queue(), ^{
[self hideLabel];
self.captureImagesButton.enabled = NO;
});
}];
}
Edit:
For a jump start, you may implement the KVO and your method as shown below. Caution: not tested!
-(void)continuousExposeAtPoint:(CGPoint)point
completion:(completion_t)completionHandler
{
AVCaptureDevice* device; // ...;
if([device isExposurePointOfInterestSupported] && [device isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]){
if([device lockForConfiguration:NULL]){
[device addObserver:self forKeyPath:#"adjustingExposure"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
context:(__bridge_retained void*)([completionHandler copy])];
[device setExposurePointOfInterest:point];
[device setExposureMode:AVCaptureExposureModeContinuousAutoExposure];
}
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object change:(NSDictionary *)change
context:(void *)context
{
AVCaptureDevice* device; // = ...;
if ([keyPath isEqual:#"adjustingExposure"]) {
if ([[change objectForKey:NSKeyValueChangeNewKey] boolValue] == NO) {
CGPoint point = device.exposurePointOfInterest;
NSLog(#"Exposure point of intereset has been set to (%f,%f)",point.x, point.y);
[device removeObserver:self forKeyPath:#"adjustingExposure"];
[device unlockForConfiguration];
completion_t block = CFBridgingRelease(context);
if (block) {
block();
}
}
}
// Be sure to call the superclass's implementation *if it implements it.
// NSObject does not implement the method.
[super observeValueForKeyPath:keyPath
ofObject:object
change:change
context:context];
}
The caveat here is, that KVO is difficult to setup. But once you managed to wrap it into a method with a completion handler it looks much nicer ;)
maybe is your code running when the run loop is in a mode other than the default mode? try this:
[self performSelector:#selector(mywork:) withObject:nil
afterDelay:delay
inModes:#[[[NSRunLoop currentRunLoop] currentMode]]];
Use dispatch after:
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
// Code
});
I personally tend to use delays in blocks on the main thread like this:
double delayInSeconds = 0.5;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
//Do your thing here
});
have you tried using a timer?
if performSelector:withObject:afterDelay: not work you can try:
[NSTimer scheduledTimerWithTimeInterval:1.5 target:self selector:#selector(captureStillImage) userInfo:nil repeats:NO];
Try this with less amount of time like 2 seconds
[self performSelector:#selector(yourMethod:) withObject:yourObject afterDelay:0.2];
You should know performSelector is work on the calling thread, if the calling thread is not executing, the selector is not called.
So I think the reason why performSelector:withObject:afterDelay: does not work is your thread execute captureMultipleImg method is not work after the delay time.
If you call captureMultipleImg with dispatch_async , same reason.
Let's say you call the method in dispatch_async
- (void)testCode
{
[self performSelector:#selector(mywork:) withObject:nil afterDelay:0.1] ;
[self endWork] ;
}
after the endWork is executed, the calling thread may be released , so - (void)mywork:(id)objis never called.
Related
I’ve not had much experience with semaphores, nor with blocks. I’ve seen various suggestions for how to turn an asynchronous call into a synchronous one. In this case I just want to wait to be sure the lens of the iPhone has changed focus before I snap another picture.
I’ve added a completion block (with a little routine to prove that I’m seeing it). But how to block the rest of my code (running on the main thread) until I get the completion callback?
- (void) changeFocusSettings
{
if ([SettingsController settings].useFocusSweep)
{
// increment the focus setting
float tmp = [SettingsController settings].fsLensPosition;
float fstmp =[[SettingsController settings] nextLensPosition: [SettingsController settings].fsLensPosition]; // get next lensposition
[SettingsController settings].fsLensPosition = fstmp ;
tmp = [SettingsController settings].fsLensPosition;
if ([self.captureDevice lockForConfiguration: nil] == YES)
{
__weak typeof(self) weakSelf = self;
[self.captureDevice setFocusModeLockedWithLensPosition:tmp
completionHandler:^(CMTime syncTime) {
NSLog(#"focus over..time = %f", CMTimeGetSeconds(syncTime));
[weakSelf focusCompletionHandler : syncTime];
}];
}
}
}
- (bool) focusCompletionHandler : (CMTime)syncTime
{
NSLog(#"focus done, time = %f", CMTimeGetSeconds(syncTime));
return true;
}
changeFocusSettings is called from another routine entirely. I image some kind of semaphore set just inside changeFocusSettings and then the focuscompletionHandler resets it. But the details are beyond me.
Thank you.
I worked through it myself, it wasn't hard at all and it appears to be working. Here's the code in case it helps someone else. And if you happen to spot an error, please let me know.
dispatch_semaphore_t focusSemaphore;
...
- (bool) focusCompletionHandler : (CMTime)syncTime
{
dispatch_semaphore_signal(focusSemaphore);
return true;
}
- (void) changeFocusSettings
{
focusSemaphore = dispatch_semaphore_create(0); // create semaphone to wait for focuschange to complete
if ([SettingsController settings].useFocusSweep)
{
// increment the fsLensposition
float tmp = [SettingsController settings].fsLensPosition;
float fstmp =[[SettingsController settings] nextLensPosition: [SettingsController settings].fsLensPosition]; // get next lensposition
[SettingsController settings].fsLensPosition = fstmp ;
tmp = [SettingsController settings].fsLensPosition;
NSLog(#"focus setting = %f and = %f", tmp, fstmp);
if ([self.captureDevice lockForConfiguration: nil] == YES)
{
__weak typeof(self) weakSelf = self;
[self.captureDevice setFocusModeLockedWithLensPosition:tmp
completionHandler:^(CMTime syncTime) {
[weakSelf focusCompletionHandler : syncTime];
}];
dispatch_semaphore_wait(focusSemaphore, DISPATCH_TIME_FOREVER);
}
}
}
I've made an NSOperation subclass with a start method containing a call to a method which has a completion block. The completion block contains code which marks the operation as finished (KVO). That block seems to typically be executed on the main thread (rather than my NSOperationQueue's background thread).
What I'm seeing is that the operation doesn't appear to ever get marked as finished, and I assume this is because of the threading issue. Is there some way I can get the KVO calls to occur on the correct thread, and thus allow my operation to terminate properly?
The method with the completion block is 3rd party code, so I'd prefer not to have to alter it.
EDIT: Here's the relevant code.
-(void)start
{
[self willChangeValueForKey:#"isExecuting"];
_isExecuting = YES;
[self didChangeValueForKey:#"isExecuting"];
NSData *mutableData = [NSData thisIsWhereMyDataIsCreated];
[self.peripheral writeData:mutableData characteristicUUID:[CBUUID transmitCharacteristicUUID] serviceUUID:[CBUUID serviceUUID] completion:^(CBCharacteristic * _Nullable characteristic, NSError * _Nullable error) {
[self markAsFinished];
}];
}
-(void)markAsFinished
{
NSLog(#"Finishing op...");
[self willChangeValueForKey:#"isExecuting"];
[self willChangeValueForKey:#"isFinished"];
_isExecuting = NO;
_isFinished = YES;
[self didChangeValueForKey:#"isExecuting"];
[self didChangeValueForKey:#"isFinished"];
self.onComplete();
}
And it's the markAsFinished: method that's being called on the main thread, via the completion block for the writeData... method.
I'm working on the chat app, in chat screen my app need to check new message every 5s. I want to do that in background thread so my app will be not blocked UI. I tried the code below but when user typing the message, the UI seems to be blocked. Also, I cannot fire this task when user exit the chat screen.
This is my code I tried:
getLatestMessagesWithInterval() is called in viewwillAppear()
-(void) getLatestMessagesWithInterval
{
NSLog(#"GET MESSAGE INTERVAL");
[self retrieveLatestChatMessages];
// Call this method again using GCD
dispatch_queue_t q_background = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
double delayInSeconds = 5.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, q_background, ^(void){
[self getLatestMessagesWithInterval];
});
}
-(void) retrieveLatestChatMessages
{
if([[NSUserDefaults standardUserDefaults] boolForKey:#"LoggedIn"]) {
NSDictionary * userDictionary = [[NSUserDefaults standardUserDefaults] dictionaryForKey:#"SessionDictionary"];
.....
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[LSDataManager sharedDataManager] getLatestMessagesWithAuthKey:authenKey andLimit:limit withBlock:^ (NSDictionary* responseDict)
{
if (responseDict) {
[self loadDataFromServer:responseDict];
NSArray* lastMessageArray= nil;
//filter message data
if (self.MessagesArray.count >0) {
//perform data
self.TempdataSource = [[[ContentManager sharedManager] generateConversation:lastMessageArray withSenderID:self.senderID] mutableCopy];
//compare 2 arrays
if ([self.TempdataSource count] == [self.dataSource count]) {
NSLog(#"both are same");
}
else{
NSLog(#"both are different");
self.dataSource = [self.TempdataSource mutableCopy];
dispatch_async(dispatch_get_main_queue(), ^(){
//Add method, task you want perform on mainQueue
[self refreshMessages];
});
}
}
}
}
}];
});
}
}
I have called retrieveLatestChatMessages() to get message from server. the server will return in block. After performing the data, I reload tableview in main thread. Pls. help me to correct it. Thanks in advance.
Communicate with API asynchronously for example use AFNetworking and your UI will not be blocked, also you can set timer for every second and call something like that
if (numberOfSeconds % 5 == 0) {
numberOfSeconds = 0;
retrieveLatestChatMessages()
}
Two options
Option 1
Call that method every 5 seconds using NSTimer.
I would recommend not to use it.
Option 2
Have an app TCP based for chat messages.
This would be best option for chat messages.
Edit 1
learn how to use TCP as per your language. For iOS TCP follow link
http://www.tekritisoftware.com/sites/default/files/Socket_Programing_for_IOS.pdf
I'm trying to do a share operation where I call a function with async block but in my next if statement I need to get the value which is completed in the block to continue. This is my code which will highlight more detail. I heard about NSLock and tried using it but it didnt work, may be I'm doing something lock, I'm not much familiar with locks.
-(void) shareOperation
{
__block NSString *resultText;
BOOL continueSharing;
NSLock *conditionLock=[[NSLock alloc] init];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
[conditionLock lock];
[self performSomeAsynchronousOperation completionBlock:^(NSError *tError, bool status)
{
dispatch_async(dispatch_get_main_queue(),
^{
if (status)
{
resultText = #"Operation completed. Would you like to continue?";
}
else
{
resultText = #" Operation failed. Would you still like to continue?";
}
UIAlertView *result = [UIAlertView alertViewWithTitle:nil message:resultText cancelButtonTitle:#"Cancel" otherButtonTitles:[NSArray arrayWithObjects:#"OK",nil] onDismiss:^(int buttonIndex)
{
NSLog(#"selected button index: %d",buttonIndex);
if (buttonIndex == 0)
{
continueSharing=YES;
[conditionLock unlock];
NSLog(#"We are continuing sharing :)");
}
}onCancel:^{
continueSharing=NO;
[conditionLock unlock];
NSLog(#"cancelled");
}]; [result show];
});
}];
});
}
//should continue only after earlier if block finishes, ie after getting the continueSharing value
if (continueSharing)
{
[self performSomeAnotherAsynchronousOperation];
}
}
Rather than using locks (which are only designed to ensure that there is not simultaneous access to some shared resource), you could use semaphores (which are designed so that one thread can wait for a signal from another thread, which is needed here).
So, you should create a semaphore:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
The alert view completion blocks would signal that semaphore:
dispatch_semaphore_signal(semaphore);
And where you want to wait for that signal (before performSomeAnotherAsynchronousOperation):
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
I tweaked your code a bit, but most notably, changed it so that it would not block the main queue (which you never want to do) by making sure the dispatch_semaphore_wait is done in a background queue. Also note that the dispatch_semaphore_signal is not inside the if statement. This resulted in:
- (void)shareOperation
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
__block BOOL continueSharing = NO;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self performSomeAsynchronousOperationWithCompletionBlock:^(NSError *tError, bool status){
dispatch_async(dispatch_get_main_queue(),^{
NSString *resultText;
if (status)
resultText = #"Operation completed. Would you like to continue?";
else
resultText = #"Operation failed. Would you still like to continue?";
UIAlertView *alertView = [UIAlertView alertViewWithTitle:nil message:resultText cancelButtonTitle:#"Cancel" otherButtonTitles:#[#"OK"] onDismiss:^(int buttonIndex) {
NSLog(#"selected button index: %d",buttonIndex);
if (buttonIndex == 0) {
continueSharing = YES;
NSLog(#"We are continuing sharing :)");
}
dispatch_semaphore_signal(semaphore);
} onCancel:^{
dispatch_semaphore_signal(semaphore);
NSLog(#"cancelled");
}];
[alertView show];
});
}];
// should continue only after earlier if block finishes, ie after getting the continueSharing value
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
if (continueSharing)
[self performSomeAnotherAsynchronousOperation];
});
}
Even better, you should not use any blocking mechanism like semaphores (certainly not on the main queue), but rather one should simply have the completion block for the alert view initiate the next step of the process directly, itself. I understand that you say that this is not practical in your scenario, but it is generally the correct way to handle these scenarios.
You can use the delay block
dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, 0.01 * NSEC_PER_SEC);
dispatch_after(delayTime, dispatch_get_main_queue(), ^(void){
})
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