How to check if NSOperationQueue is finished and if any operation failed? - objective-c

I'm trying to parse some XML files in the background so that the UI doesn't freeze. I have to check two things:
NSOperationQueue is finished?
NSOperation - parsing did fail?
I have a class that subclasses NSOperation and a delegate is called if the parsing failed. Operations in the queue are limited to one simultaneously.
My problem is that I can't rely on the fact that the failed message is executed before I get the queue did finish message. Sometimes I don't get a failed message before I get the finished message. Then, for example, I have this order:
Operation 1 Successful
Operation 2 Successful
OperationQueue finished
Operation 3 Failed
All messages are sent to the main thread. After I get the finished message I want to proceed in my code, but only if all operations were successful. How can I handle the problem that the delegate message is called after my queue is finished.
This are some parts of my code:
//XMLOperation.m
- (void)main {
NSLog(#"Operation started");
if ([self parseXML]) {
[self performSelectorOnMainThread:#selector(finishedXMLParsing) withObject:nil waitUntilDone:NO];
} else {
[self performSelectorOnMainThread:#selector(failedXMLParsing) withObject:nil waitUntilDone:NO];
}
}
NSLog(#"Operation finished");
}
//StartController.m
[self.xmlParseQueue addObserver:self forKeyPath:#"operations" options:0 context:NULL];
...
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context
{
if (object == self.xmlParseQueue && [keyPath isEqualToString:#"operations"]) {
if ([self.xmlParseQueue.operations count] == 0) {
// Do something here when your queue has completed
NSLog(#"queue has completed");
}
}
else {
[super observeValueForKeyPath:keyPath ofObject:object
change:change context:context];
}
}

This probably happens because your KVO notification for the operations property of the queue is not necessarily delivered on the main thread while your finished/failed notifications are.
You should ensure that the completion notification is performed on the main thread as well, so that the order of your notifications is defined.

Resume this question and paste my code solution here.
void RunInMainThread(void (^block)(void))
{
if (!block) {
return;
}
if ([NSThread isMainThread]) {
block();
} else {
dispatch_async(dispatch_get_main_queue(), block);
}
}
#interface MyOperationQueue : NSOperationQueue
#property (nonatomic, assign) NSUInteger totalCount;
#property (nonatomic, copy) dispatch_block_t allOperationCompletionBlock;
#end
#implementation MyOperationQueue
- (void)addOperation:(NSOperation *)op
{
[super addOperation:op];
__weak typeof(self) weakSelf = self;
self.totalCount++;
DDLogVerbose(#"Added a operation, total: %ld operation(s).", self.totalCount);
op.completionBlock = ^ {
weakSelf.totalCount--;
DDLogVerbose(#"Finished a operation, left: %ld operation(s).", weakSelf.totalCount);
if (weakSelf.totalCount == 0) {
if (weakSelf.allOperationCompletionBlock) {
RunInMainThread(weakSelf.allOperationCompletionBlock);
}
}
};
}
#end

Related

Obj-C return to a block from a delegate method?

I'm writing a mac app that runs its own web server, using the GCDWebServer library (https://github.com/swisspol/GCDWebServer). My app delegate handles GET requests like so:
__weak typeof(self) weakSelf = self;
[webServer addDefaultHandlerForMethod:#"GET"
requestClass:[GCDWebServerRequest class]
processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
return [weakSelf handleRequest:request];
}];
And then the handleRequest method returns the response data, something like:
return [GCDWebServerDataResponse responseWithHTML:#"<html><body><p>Hello World!</p></body></html>"];
So far so good. Except now I want the handleRequest method to use NSSpeechSynthesizer to create an audio file with some spoken text in it, and then wait for the speechSynthesizer:didFinishSpeaking method to be called before returning to the processBlock.
// NSSpeechSynthesizerDelegate method:
- (void)speechSynthesizer:(NSSpeechSynthesizer *)sender didFinishSpeaking:(BOOL)success
{
NSLog(#"did finish speaking, success: %d", success);
// return to processBlock...
}
Problem is, I have no idea how to do this. Is there a way to return from the speechSynthesizer:didFinishSpeaking method into the processBlock defined above?
You need to run the speech synthesizer on a separate thread with its own run loop, and use a lock to allow your request thread to wait for the operation to complete on the speech thread.
Assuming the web server maintains its own thread(s) and runloop, you can use your app's main thread to run the speech synthesizer, and you can use NSCondition to signal completion to the web response thread.
A basic (untested) example (without error handling):
#interface SynchroSpeaker : NSObject<NSSpeechSynthesizerDelegate>
- (id)initWithText:(NSString*)text outputUrl:(NSURL*)url;
- (void)run;
#end
#implementation SynchroSpeaker
{
NSCondition* _lock;
NSString* _text;
NSURL* _url;
NSSpeechSynthesizer* _synth;
}
- (id)initWithText:(NSString*)text outputUrl:(NSURL*)url
{
if (self = [super init])
{
_text = text;
_url = url;
_lock = [NSCondition new];
}
return self;
}
- (void)run
{
NSAssert(![NSThread isMainThread], #"This method cannot execute on the main thread.");
[_lock lock];
[self performSelectorOnMainThread:#selector(startOnMainThread) withObject:nil waitUntilDone:NO];
[_lock wait];
[_lock unlock];
}
- (void)startOnMainThread
{
NSAssert([NSThread isMainThread], #"This method must execute on the main thread.");
[_lock lock];
//
// Set up your speech synethsizer and start speaking
//
}
- (void)speechSynthesizer:(NSSpeechSynthesizer *)sender didFinishSpeaking:(BOOL)success
{
//
// Signal waiting thread that speaking has completed
//
[_lock signal];
[_lock unlock];
}
#end
It's used like so:
- (id)handleRequest:(id)request
{
SynchroSpeaker* speaker = [[SynchroSpeaker alloc] initWithText:#"Hello World" outputUrl:[NSURL fileURLWithPath:#"/tmp/foo.dat"]];
[speaker run];
////
return response;
}
GCDWebServer does run into its own threads (I guess 2 of them) - not in the main one. My solution needed to run code in Main Thread when calling the ProcessBlock.
I found this way that suits my needs:
First declare a weak storage for my AppDelegate: __weak AppDelegate *weakSelf = self;. Doing so I can access all my properties within the block.
Declare a strong reference to AppDelegate from within the block like so: __strong AppDelegate* strongSelf = weakSelf;
Use NSOperationQueue to align the operation on mainThread:
[[NSOperationQueue mainQueue] addOperationWithBlock:^ {
//Your code goes in here
NSLog(#"Main Thread Code");
[strongSelf myMethodOnMainThread];
}];
In this way myMethodOnMainThread surely will run where it's supposed to.
For sake of clarity I quote my relevant code section:
webServer = [[GCDWebServer alloc] init];
webServer.delegate = self;
__weak AppDelegate *weakSelf = self;
// Add a handler to respond to GET requests
[webServer addDefaultHandlerForMethod:#"GET"
requestClass:[GCDWebServerRequest class]
asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
__strong AppDelegate* strongSelf = weakSelf;
[[NSOperationQueue mainQueue] addOperationWithBlock:^ {
//Your code goes in here
NSLog(#"Main Thread Code");
[strongSelf myMethodOnMainThread];
}];
GCDWebServerDataResponse* response = [GCDWebServerDataResponse responseWithJSONObject:packet];
completionBlock(response);
}];
GCWebServer supports fully asynchronous responses as of version 3.0 and later [1].
[webServer addDefaultHandlerForMethod:#"GET"
requestClass:[GCDWebServerRequest class]
asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
// 1. Trigger speech synthesizer on main thread (or whatever thread it has to run on) and save "completionBlock"
// 2. Have the delegate from the speech synthesizer call "completionBlock" when done passing an appropriate response
}];
[1] https://github.com/swisspol/GCDWebServer#asynchronous-http-responses

"disable" button-->method until operation is done

I am using the following method that invoked by pressing a button thru sprite builder.
- (void)method {
//static dispatch_once_t pred; //
//dispatch_once(&pred, ^{ // run only once code below
[self performSelector:#selector(aaa) withObject:nil afterDelay:0.f];
[self performSelector:#selector(bbb) withObject:nil afterDelay:1.f];
[self performSelector:#selector(ccc) withObject:nil afterDelay:1.5f];
[self performSelector:#selector(ddd) withObject:nil afterDelay:4.f];
[self performSelector:#selector(eee) withObject:nil afterDelay:4.5f];
CCLOG(#"Received a touch");
//}); //run only once code above
}
as you can see from the comments i tried running it once. that works good, but if a user comes back to this scene, it's disabled until you restart the app.
how can i block this method from being executed a second time until the first time is done.
i know the code is rough, i'm just learning here....
thanks in advance.
Add a BOOL instance variable which serves as a flag as to whether or not this action is taking place. As soon as the method starts, check the flag. If you need to execute, set the flag.
Add another performSelector:withObject:afterDelay: which calls a method to reset the flag back.
#implementation SomeClass {
BOOL _onceAtATime;
}
- (void)method {
#synchronized(self) {
if (!_onceAtATime) {
_onceAtATime = YES;
// do all the stuff you need to do
[self performSelector:#selector(resetOnceAtATime)
withObject:nil
afterDelay:delay];
// where delay is sufficiently long enough for all the code you
// are executing to complete
}
}
}
- (void)resetOnceAtATime {
_onceAtATime = NO;
}
#end
A simpler way would be to use a serial NSOperationQueue as such (in Swift):
class ViewController: UIViewController {
let queue: NSOperationQueue
required init(coder aDecoder: NSCoder) {
queue = NSOperationQueue()
queue.maxConcurrentOperationCount = 1
super.init(coder: aDecoder)
}
#IBAction func go(sender: AnyObject) {
if (queue.operationCount == 0) {
queue.addOperationWithBlock() {
// do the first slow thing here
}
queue.addOperationWithBlock() {
// and the next slow thing here
}
// ..and so on
}
else {
NSLog("busy doing those things")
}
}
}

Wait for two async methods to complete

I'd like to init a model, let the model do some async stuff and present a new viewcontroller once completed. But how do i wait for the two async methods to be completed and how do I setup the callback method?
Pseudocode
In my StartViewController.m:
-(void)openArticle
{
article = [Article initWithObject:someObject];
article.callback = changeView;
}
-(void)changeView
{
[self presentViewController:someController];
}
In my ArticleModel.m:
-(void)initWithObject:someObject
{
[self loadImage]
[self geoCode]
}
-(void)loadImage
{
runAsyncMethod: success:^() // This one is actually a AFNetworking setImageWithURLRequest
}
-(void)geoCode
{
runAnotherAsyncMethod: success:^() // This one is actually a geocodeAddressString operation
}
You can achieve this using dispatch_groups
- (void)initWithObject:(id)someObject
{
self = [super init];
if (self) {
self.dispatch_group = dispatch_group_create();
[self loadImage]
[self geoCode]
dispatch_group_notify(self.dispatch_group, dispatch_get_main_queue(), ^{
NSLog(#"Push new view controller");
});
}
return self;
}
- (void)loadImage
{
dispatch_group_enter(self.dispatch_group);
__weak __typeof(self) weakSelf = self;
runAsyncMethod: success:^{
__typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf.dispatch_group) {
dispatch_group_leave(strongSelf.dispatch_group); // You need to ensure that this is called in both success and failure
}
}
}
- (void)geoCode
{
dispatch_group_enter(self.dispatch_group);
__weak __typeof(self) weakSelf = self;
runAnotherAsyncMethod: success:^{
__typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf.dispatch_group) {
dispatch_group_leave(strongSelf.dispatch_group);
}
}
}
You do not wait. If you wait, it isn't asynchronous! You would be losing the entire point of asynchronous if you were to wait.
What you do is, when your success handler is called, you step out to the main thread (just in case you got called back on a background thread) and now do whatever you need to do. In other words, you just let your success handler get called whenever it happens to get called.
In your case, you might like to chain the things you want to do:
Call loadImage
In its callback, call geoCode
In its callback, step out to the main thread and present the new view controller.
You can use dispatch_group so that when a method is over, it just leaves the group. I use a similar code myself and it works like a charm.
- (void)initWithObject:someObject {
// Create a dispatch group
dispatch_group_t group = dispatch_group_create();
[self loadImageWithDispatchGroup:group];
[self geoCodeWithDispatchGroup:group];
// Here we wait for all the requests to finish
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// Do whatever you need to do when all requests are finished
});
}
- (void)loadImageWithDispatchGroup:(dispatch_group_t)group {
dispatch_group_enter(group);
runAsyncMethod: success:^() // This one is actually a AFNetworking setImageWithURLRequest
// In your success or failure AFNetworking method, call this as soon as the request ended
dispatch_group_leave(group);
}
- (void)geoCodeWithDispatchGroup:(dispatch_group_t)group {
dispatch_group_enter(group);
runAnotherAsyncMethod: success:^() // This one is actually a geocodeAddressString operation
// In your success async geocode callback method, call this as soon as the request ended
dispatch_group_leave(group);
}
I do not known your needs but native GCD way to wait several asynch tasks is
void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html#//apple_ref/c/func/dispatch_barrier_async

How to pass a SEL to dispatch_async method

I am trying to create a generic method that takes a SEL as a parameter and passes it to dispatch_async for execution, but i am clueless how to execute the passed in SEL.
Can anyone here help me please.
// Test.m
-(void) executeMe
{
NSLog(#"Hello");
}
- (void)viewDidLoad
{
[super viewDidLoad];
SEL executeSel = #selector(executeMe);
[_pInst Common_Dispatch: executeSel];
}
// Common.m
-(void) Common_Dispatch:(SEL) aSelector
{
dispatch_async(iDispatchWorkerQueue, ^(void) {
// How to execute aSelector here?
});
}
You need to also have a "target" parameter on your Common_Dispatch method since you need to call the selector on a specific object.
- (void)viewDidLoad {
[super viewDidLoad];
SEL executeSel = #selector(executeMe);
[_pInst Common_Dispatch:executeSel target:self];
}
- (void)Common_Dispatch:(SEL)aSelector target:(id)target {
dispatch_async(iDispatchWorkerQueue, ^(void) {
[target performSelector:aSelector];
});
}
BTW - standard naming conventions state that method names should begin with lowercase and use camelCase. Your method should be commonDispatch.
Alternatively, you could use a block parameter, e.g.
- (void)commonDispatch:(void (^)(void))block
{
dispatch_async(iDispatchWorkerQueue, block);
}
You'd then invoke that as:
[_pInst commonDispatch:^{
[self executeMe];
}];
This way, you could use this dispatcher to call methods like executeMe which take no parameters, or to dispatch methods that take lots of parameters, e.g.:
[_pInst commonDispatch:^{
[self executeOtherMethodForURL:url requestType:type priority:priority];
}];
Or more complicated situations, too:
[_pInst commonDispatch:^{
[self executeOtherMethodForURL:url requestType:type priority:priority];
dispatch_async(dispatch_get_main_queue(), ^{
// update my UI to say that the request is done
});
}];
You simply call the performSelector method, like this:
[self performSelector:aSelector];
There are other useful overrides to performSelector you'll find.
Edit
The target of the selector will also have to be passed as a param:
// Test.m
-(void) executeMe
{
NSLog(#"Hello");
}
- (void)viewDidLoad
{
[super viewDidLoad];
SEL executeSel = #selector(executeMe);
[_pInst Common_Dispatch: executeSel target:self];
}
// Common.m
-(void) Common_Dispatch:(SEL) aSelector target:(id)target
{
dispatch_async(iDispatchWorkerQueue, ^(void) {
[target performSelector:aSelector];
});
}

Completion Blocks ? Asynchronous processes embedded in synchronous workflow

Long time lurker, first time poster. I'm relatively new to objective-C so my apologies if I'm asking something fairly simple. My google & stack overflow-fu has let me down here, so I figured somebody could maybe help.
I have a synchronous process executing, say, three functions in a row - call it A -> B-> C , where task A executes, followed by B, followed by C.
Now, B involves an asynchronous process with a delegate callback for completion. But B must complete before C is executed, so I need some mechanism such that C is not triggered before B has finished. I imagine there must be a common design pattern for this problem?
Initially naive solution would be -
execute A
execute B
while (!B finished) {}
execute C
...but this seems really lame.
I suspect I can do this with some kind of block, but for the life of me I just can't figure it out. Could anyone help?
appreciate any assistance!
Guillaume
Thanks for all the feeback - apologies for not responding sooner. I've now resolved this in a slightly different way to the suggestions:
Firstly, I extended NSObject to have the following method -
#import "NSObject+LTExtensions.h"
#implementation NSObject (Testing)
- (void) performSelectorWithBlock: (SEL) selector withSemaphore:(dispatch_semaphore_t)semaphore
{
[self performSelector:selector]; // This selector should complete the semaphore
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_release(semaphore);
}
#end
This allows me to execute a block via a selector. When the block executes, the thread on which it is executed will wait until signaled to proceed by a specific dispatch semaphore.
What we can then do is as follows:
Call A
Create a dispatch semaphore and define a selector which executes B
Call the method defined above to execute B and wait for the selector to complete
When B is completed (via a delegate callback), it signals the dispatch semaphore to suspend the wait
I then execute C
So we have
A
B -> Asynchronous with delegate callback
C
Here's a simple example of how the above is implemented
-(void) methodA {
// ... do something
// Assign your semaphore (this is a dispatch_semaphore_t)
self.semaphore = dispatch_semaphore_create(0);
[self performSelectorWithBlock:#selector(methodB) withSemaphore:semaphore];
[self methodC];
}
-(void) methodB {
// ... do whatever needs to be done asynchronously
CFRunLoopRun();
}
-(void) methodBDelegateCallBack {
// This is called when B completes
// Signal completion
dispatch_semaphore_signal(self.semaphore);
CFRunLoopStop(CFRunLoopGetCurrent());
}
-(void) methodC {
...
}
Works very well without any issues (but I am new to Obj C, so there may be glaring issues with my approach).
Another approach to this problem might be the following: create an helper object for the async task and copy a completion block when the task is called. Call the completion block using the delegate methods once the async task is finished. As a result we might execute the tasks in order like the following:
FSTask *taskA = [FSTask taskWithName:#"Task A"];
FSAsyncTask *taskB = [FSAsyncTask asyncTaskWithName:#"Task B"];
FSTask *taskC = [FSTask taskWithName:#"Task C"];
[taskA performTaskWithCompletionBlock:^ (NSString *result) {
NSLog(#"%#", result);
[taskB performTaskWithCompletionBlock:^ (NSString *result) {
NSLog(#"%#", result);
[taskC performTaskWithCompletionBlock:^ (NSString *result) {
NSLog(#"%#", result);
}];
}];
}];
So how is this achieved? Well, look at the task objects below ...
FSTask.m - synchronous work on main thread ...
#interface FSTask ()
#property (nonatomic, copy) NSString *name;
#end
#implementation FSTask
#synthesize name = _name;
+ (FSTask *)taskWithName:(NSString *)name
{
FSTask *task = [[FSTask alloc] init];
if (task)
{
task.name = name;
}
return task;
}
- (void)performTaskWithCompletionBlock:(void (^)(NSString *taskResult))block
{
NSString *message = [NSString stringWithFormat:#"%#: doing work on main thread ...", _name];
NSLog(#"%#", message);
if (block)
{
NSString *result = [NSString stringWithFormat:#"%#: result", _name];
block(result);
}
}
#end
FSAsyncTask.m - asynchronous work on background thread ...
#interface FSAsyncTask ()
#property (nonatomic, copy) void (^block)(NSString *taskResult);
#property (nonatomic, copy) NSString *name;
- (void)performAsyncTask;
#end
#implementation FSAsyncTask
#synthesize block = _block;
#synthesize name = _name;
+ (FSAsyncTask *)asyncTaskWithName:(NSString *)name
{
FSAsyncTask *task = [[FSAsyncTask alloc] init];
if (task)
{
task.name = name;
}
return task;
}
- (void)performTaskWithCompletionBlock:(void (^)(NSString *taskResult))block
{
self.block = block;
// the call below could be e.g. a NSURLConnection that's being opened,
// in this case a NSURLConnectionDelegate method will return the result
// in this delegate method the completion block could be called ...
dispatch_queue_t queue = dispatch_queue_create("com.example.asynctask", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^ {
[self performAsyncTask];
});
}
#pragma mark - Private
- (void)performAsyncTask
{
for (int i = 0; i < 5; i++)
{
NSString *message = [NSString stringWithFormat:#"%d - %#: doing work on background thread ...", i, _name];
NSLog(#"%#", message);
[NSThread sleepForTimeInterval:1];
}
// this completion block might be called from your delegate methods ...
if (_block)
{
dispatch_async(dispatch_get_main_queue(), ^ {
NSString *result = [NSString stringWithFormat:#"%#: result", _name];
_block(result);
});
}
}
#end
You can assign a block property to B where it would be used to execute a block of code before calling the delegate method. something like:
#property (nonatomic, copy)void(^yourBlock)(id blockParameter);
So, after calling B's delegate, you could call upon this block and execute it. Inside this block, you can call C's method.
the way I handled this is.
I created a NSMutableDictionary before the async call.
Then i make the async call. and do a check for the value I am waiting for
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[AsyncCallClass asyncCall:^{
#synchronized(dictionary) {
[dictionary setValue:myValue forKey:#"result"];
}
}];
while (true){
#synchronized(dictionary){
if ([dictionary valueForKey:#"resultValue"] != nil){
break;
}
}
[NSThread sleepForTimeInterval:.25];
}
MyResultClass *result = [dictionary valueForKey:#"resultValue"];
you can add time out for this too to stop it from being an infinite loop. but this is my solution. and it seems to work pretty well.
Here is the typical code I use to do such things (adapt the completionBlock signature and method names to your needs of course)
typedef void (^BCompletionBlock)(void);
#interface B : NSObject <BDelegate>
#property(nonatomic, copy) BCompletionBlock completionBlock;
-(void)doAsynchronousActionWithCompletion:(BCompletionBlock)aCompletionBlock;
#end
#implementation B
-(void)doAsynchronousActionWithCompletion:(BCompletionBlock)aCompletionBlock
{
// Store the completion block for later use
self.completionBlock = aCompletionBlock;
// Then execute your asynchronous action, that will call some delegate method when done
[self doYourAsynchronousActionWithDelegate:self];
}
-(void)yourBDelegateMethodCalledWhenDone
{
// Upon your async task completion, call your completion block then
if (self.completionBlock) self.completionBlock();
}
#end
Then here is an example usage:
-(void)doActions
{
[a doSynchronousAction];
[b doAsynchronousActionWithCompletion:^{
[c doSynchronousAction];
// A,B,C are now done
}];
}
I do this quite all the time to "convert" actions that uses delegate methods (to tell me when they are done) to actions that uses completionBlocks (have some classes to do this for UIAlertViews, UIActionsSheets, and many more cases for example) and it works like a charm.
I find it much more easier to use completionBlocks than the delegate mechanism in such cases.
You can also pass C in a block like so...
define a custom block
typedef void(^myCompletion)(BOOL complete);
Create your B method
-(void)performBWithCompletionBlock:(myCompletion)complete;
{
// do your things
[self.delegate delegateCallback];
complete(YES);
}
then create BG / async ABC
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // now we're on a BG queue to perform our async tasks
[self performA];
[self performBWithCompletionBlock:^(BOOL complete) {
if (complete == YES)
[self performC];
}];
});
If you want C to be on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
[self performC];
});