In my iOS app, I would like to wait for a condition to become true before updating the UI.
I am doing like this:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
while (!condition) NSLog("waiting for the condition");
dispatch_sync(dispatch_get_main_queue, ^{
//update the UI here
});
});
The code above works just fine but I would like to ask if using the while loop to do the waiting job is good or not, and if there is any better way.
Thanks!
---Update
The condition is actually a combination of 4 BOOL variables. Each variable associates with a request for content from the server. I'm using AFNetworking framework. In the completion block of each of the 4 requests, I will set the associated BOOL variable to YES.
So, the actual while loop is like this:
while (!([MyRequest request1].isFinished && [MyRequest request2].isFinished && [MyRequest request3].isFinished && [MyRequest request4].isFinished)) NSLog("waiting for the condition");
In the revised question, it sounds like you have four AFNetworking operations that you want to be dependent upon. That's far easier. You might just add a new operation, and make it dependent upon the other four operations:
NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// update UI
}];
}];
[operation addDependency:requestOneOperation];
[operation addDependency:requestTwoOperation];
[operation addDependency:requestThreeOperation];
[operation addDependency:requestFourOperation];
[queue addOperation:operation];
The addDependency mechanism essentially does KVO of isFinished of each of those other four operations for you. It's one of the joys of using an NSOperation-based framework like AFNetworking. This sort of dependency is really easy to do.
Original answer:
If you had to do this, you might use a semaphore instead, e.g., you'd create a semaphore:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
You'd have your async block wait for that:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
dispatch_semaphore_wait(semaphore);
dispatch_sync(dispatch_get_main_queue, ^{
//update the UI here
});
});
And when the condition is satisfied, the code that would have otherwise set this condition flag would, instead:
dispatch_semaphore_signal(semaphore);
Having said that, I'd rather not see a queue blocked (even a concurrent global queue) like this unless absolutely necessary. If that other code could issue the semaphore signal, I'm not sure why it couldn't just initiate the UI update itself. If I did use this semaphore technique, at the very least I'd have this waiting process taking place on a queue of my own creation, not a global queue.
Another approach which you can use in many situations, and which I'd probably prefer, is to employ key value observing:
For example, I could observe the changing of they someProperty property of an object called obj like so:
[obj addObserver:self forKeyPath:#"someProperty" options:NSKeyValueObservingOptionNew context:NULL];
I would then implement observeValueForKeyPath:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:#"someProperty"])
{
NSLog(#"update UI here");
}
}
Whenever the someProperty property of my obj object is updated, my observeValueForKeyPath method would be called.
FYI, I'd also make sure that before this object gets released, I'd remove the observer of obj:
[obj removeObserver:self forKeyPath:#"someProperty"];
Clearly, this assumes that someProperty is Key Value Coding Compliant. But if it is, this is a great technique.
While the general pattern here is correct (Apple refers to it as "call callback"), the while(!condition) bit is also known as a "spin lock", and is definitely not the best way to wait for a condition. Consider using NSTimer or NSRunLoop instead.
Related
I have a thread call in objective C and i want once this thread ends i want to return a value ;the value will be changed inside the thread
So the method must not return the value unless the tread ends
Here is the code i use:
[NSThread detachNewThreadSelector:#selector(CheckBeforePrint2) toTarget:self withObject:nil];
This is My Full Code
- (NSString *) getStatusPrinter:(NSString *) printerSerialNumber
{
self.printerSN = printerSerialNumber;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *Result = #"-1";
[NSThread sleepForTimeInterval:2.0f];
[NSThread detachNewThreadSelector:#selector(CheckBeforePrint) toTarget:self withObject:Result];
[pool release];
return Result;
}
Now i want to wait for the value of Result and return it i am using
cocoa
And i am returning the value to another app
Can anyone help in that.
Thanks
What you are doing here requires the use of a semaphore for example. If there is nothing more to it than you are providing here then a completion block to a background running method is the best way to do it. See option 2 below
Either way, why do you want the parent thread (the thread dispatching a new thread) to wait for another thread? If the parent thread is waiting, it is locked until the dispatched thread is done (according to your requirement). This situation is redundant because the whole point of dispatching another thread is so that the parent thread can continue with other things. Unless of course the parent thread needs to wait for multiple threads, then it makes sense to lock it.
Having said that, its best to just let the dispatching thread / parent thread do the processing that you are dispatching on to another thread. Im only saying this given the details you have provided.
OPTION 1 use a semaphore
Use a semaphore to lock and unlock parent thread
-(void)getStatusPrinter()
{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[NSThread detachNewThreadSelector:#selector(checkBeforePrint2) toTarget:self withObject: semaphore];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
[self print]; // this will run after semaphore is unlocked
}
-(void)checkBeforePrint2:(dispatch_semaphore_t)sem
{
//this is within child thread
//do some processing,
dispatch_semaphore_signal(sem);//unlock semaphore
}
But, as I mentioned before, this situation seems redundant because the parent thread waits (therefore unusable) for child thread; why can't it just do the work itself...
OPTION 2 use completion block (PREFERABLE)
Use a completion block that you pass to the child thread. This allows the parent thread to continue. If it is the main thread it remains free for UI stuff.
-(void)getStatusPrinter()
{
[self checkBeforePrint2WithCompletion: ^{
[self print];
}];
//continue with more stuff
}
-(void)checkBeforePrint2WithCompletion:(void (^ __nullable)(void))completion
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//do something before executing completion code
if(completion){
completion();
}
});
}
Disclaimer: there may be typos in this code as it was not written in an editor/ IDE. Please comment if any.
UPDATE in response to added details.
Okay, for the fact that you said you need to return the result to another application, that means the entry thread at getStatusPrinter can not be allowed to return after dispatching a new thread. If you really need to create a new thread for CheckBeforePrint then the entry thread has to wait. That to me is pointless. You can simply run everything on the entry thread.
If you are using openURL:options:completionHandler: then the entry thread doesn't need to wait. The value of result can be passed back within the completion block.
Please refer to Apple's documentation on openURL with a completion handle
I am trying to implement concurrency in objective C. I have a problem with an actions that needs to be run in a synchronized way. The problem here is that I use function that executes a block after completion.
I want to connect to a bluetooth device to run some operations and connect to the next device.
for (Beacon * beacon in beacons) {
[beacon setDelegate:self];
[beacon connectToBeacon];
}
But the connection is asynchronous. The beacon call the delegate (in this case it's the same class) method didConnectSuccess when connection is successful.
I need to wait all my operations in "beaconDidConnect" and deconnection to finish before connecting to the next device.
I currently use a combination of dispatch queue and dispatch semaphore, my semaphore is an ivar
dispatch_queue_t myCustomQueue;
myCustomQueue = dispatch_queue_create("com.example.MyCustomQueue", NULL);
for (Beacon * beacon in beacons) {
[beacon setDelegate:self];
dispatch_async(myCustomQueue, ^{
dispatch_semaphore_wait(semaphoreBluetooth, DISPATCH_TIME_FOREVER);
[beacon connectToBeacon];
});
}
In combination with
- (void)beaconDidDisconnect:(Beacon *)beacon
{
dispatch_semaphore_signal(semaphoreBluetooth);
}
Without the dispatch_async, by blocking the callback (beaconDidConnect), the wait was causing a deadlock.
I wanted to dispatch_semaphore_wait in the for loop and not in the dispatch block but the wait causes the callback to wait again, causing a deadlock.
This way it seems to work but I found it a bit ugly.
My other issue is that in my beaconDidConnect method I need to chain asynchronous call and in each waiting the previous to terminate.
All those calls have a termination block, executing when the call is done. I could write instructions in deeper and deeper block but I'd like to avoid this.
I'd need an equivalent of the javascript "promise" concept.
Currently I have something with dispatch queue and dispatch semaphore but I sometimes have deadlock for unknown reason.
Eg :
- (void)beaconConnectionDidSucceeded:(Beacon *)beacon
{
dispatch_semaphore_t semaphoreEditing = dispatch_semaphore_create(1);
dispatch_queue_t editingQueue = dispatch_queue_create("com.example.MyCustomQueue.Editing", NULL);
// First writing procedure
dispatch_async(editingQueue, ^{
dispatch_semaphore_wait(semaphoreEditing, DISPATCH_TIME_FOREVER);
[beacon writeSomeCaracteristic:caracteristic withValue:value withCompletion:^(void) {
dispatch_semaphore_signal(semaphoreEditing);
}];
});
// A unknow number of writing sequences
dispatch_async(editingQueue, ^{
dispatch_semaphore_wait(semaphoreEditing, DISPATCH_TIME_FOREVER);
[beacon writeSomeCaracteristic:caracteristic withValue:value withCompletion:^(void) {
dispatch_semaphore_signal(semaphoreEditing);
}];
});
//
// ...
//
dispatch_async(editingQueue, ^{
dispatch_semaphore_wait(semaphoreEditing, DISPATCH_TIME_FOREVER);
[beacon writeSomeCaracteristic:caracteristic withValue:value withCompletion:^(void) {
dispatch_semaphore_signal(semaphoreEditing);
}];
});
// Terminate the edition
dispatch_async(editingQueue, ^{
dispatch_semaphore_wait(semaphoreEditing, DISPATCH_TIME_FOREVER);
[beacon disconnectBeacon];
dispatch_semaphore_signal(semaphoreEditing);
});
}
I want to write clear code that execute my instructions in a sequential way.
If your asynchronous methods do have a completion handler, you can "serialize" or "chain" a number of asynchronous calls like shown below:
[self asyncFooWithCompletion:^(id result){
if (result) {
[self asyncBarWithCompletion:^(id result){
if (result) {
[self asyncFoobarWithCompletion:^(id result){
if (result) {
...
}
}];
}
}];
}
}];
Of course, this gets increasingly confusing with the number of chained asynchronous calls, and especially when you want to handle errors, too.
With a third party library which especially helps to overcome these problems (including error handling, cancellation) it may look similar as the code below:
Given:
- (Promise*) asyncFoo;
- (Promise*) asyncBar;
- (Promise*) asyncFoobar;
"Chaining" the three asynchronous methods including error handling:
[self asyncFoo]
.then(^id(id result){
... // do something with result of asyncFoo
return [self asyncBar];
}, nil)
.then(^id (id result){
... // do something with result of asyncBar
return [self asyncFoobar];
}, nil)
.then(^id(id result) {
... // do something with result of asyncFoobar
return nil;
},
^id(NSError*error){
// "catch" any error from any async method above
NSLog(#"Error: %#", error);
return nil;
});
For general info about "Promises", please read wiki article Futures and Promises.
There are number of Objective-C libraries which implement a Promise.
Have you considered use NSOperation and NSOperationQueue?
If you need to wait for every beacon to run a set of operations before continue, you can store every set of operations in a NSOperation and put all the operations inside a NSOperationQueue with a maxConcurrentLimit of 1. It might be easier to cancel/pause/terminate every single operation and the queue will take care of the concurrency.
I kept the dispatch_queue and dispatch_semaphore for the connection but for the writing actions I use a library called Sequencer I found here.
It follows the Promises principle CouchDeveloper talked about.
From a view controller, as a result of a button action, I need to create a custom object that manages a set of asynchronous remote service calls, and call the method of such object that fires those service calls. I need the view controller to wait for all the async networking operations to have finished in order to update its view. Since the networking operations are async, I don't know how I'd communicate from the custom object managing this tasks to the view controller when all operations are done.
Here is the code I currently have. The code snippet in the view controller is like this (result var is not currently used):
- (void)loadData
{
BOOL __block result = NO;
dispatch_queue_t queue = dispatch_queue_create(dataLoadQueue, NULL);
dispatch_async(queue,^{
Loader *loader = [[Loader alloc] init];
[loader loadData];
dispatch_async(dispatch_get_main_queue(), ^{
if (result) {
// Update view and notify success
}
else {
// Update view and notify error
}
});
});
dispatch_release(queue);
}
And this is the loader custom object side:
- (void)loadData
{
if ([Reachability checkNetStatus]) {
Service1 *service1 = [[Service1 alloc] init];
[service1 callAsyncService];
Service2 *service2 = [[Service2 alloc] init];
[service2 callAsyncService];
// More service calls
}
else {
// Notify network not reachable
}
}
Objects service1, service2... serviceN conform the NSURLConnectionDelegate and I notify they have finished in its connectionDidFinishLoading: by means of the NSNotificationCenter (loader object is listening for such notifications). Then, I donĀ“t know what is the correct way of making loader wait for all the networking operations, and notify back the view controller.
Thanks in advance
There are probably lots of ways you could do this. First, I don't think there's any need to use GCD in the view controller -- loader is already doing things asynchronously, so the creation of loader is fast.
As for how Loader knows when all its network operations are done, you could just keep a list of strings in a mutable array, like "1 done", "2 done", etc. that would be the same as strings sent in the user info of the notifications called in connectionDidFinishLoading:. All the services could send the same notification, but with different user info. In the selector for the observer, remove the string identical to the one in the user info, and check if the array is empty -- when it is, all your services are done. At that point, I would use a delegate method to pass back the data to the view controller. Something like this in Loader:
- (void)viewDidLoad {
[super viewDidLoad];
self.doneStrings = [#[#"1 done", #"2 done", #"3 done", #"4 done"] mutableCopy];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(notificationReceived:) name:#"SeriveFinishedNotification" object:nil];
}
-(void)notificationReceived:(NSNotification *) aNote {
[self.doneStrings removeObjectIdenticalTo:[aNote.userInfo objectForKey:#"doneString"]];
if (self.doneStrings.count == 0)
[delegate doSomethingWithTheData: theData];
}
You would probably need to some other things like handle the case where some of the network operations fail.
If you want to wait until the async tasks were done, you can use a semaphore. See the example below, the logic is pretty simply. I think you can easily adapt to your case.
//create the semaphore
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[objectManager.HTTPClient deletePath:[address addressURL] parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
//some code here executed in background
dispatch_semaphore_signal(semaphore); //sends a notification to release the semaphore
}failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//some other code here also executed in background
dispatch_semaphore_signal(semaphore); //sends a notification to release the semaphore
}];
//holds the thread until the dispatch_semaphore_signal(semaphore) is send
while (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW))
{
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]];
}
You haven't shared the details of how these asynchronous requests work, but another approach is to make these asynchronous requests NSOperation objects that you submit to a NSOperationQueue. (AFNetworking is an example of this sort of implementation.) When you do that, you can create yet another NSOperation to be triggered upon the completion of the network request operations, by make it dependent upon those network request operations. Thus it will only run when all of the network requests are done. Using an NSOperation-based solution enjoys other benefits, too (e.g. you can use setMaxConcurrentOperationCount to let you enjoy concurrency, but not run too many concurrent requests at any given time).
References
Ray Wenderlich's How To Use NSOperations and NSOperationQueues
Defining a Custom Operation Object in Apple's Concurrency Programming Guide
I am making an app that shows an animated UIImageView as a custom way of indicating that the app is busy. I'm using an NSOperationQueue for file uploads, and I'd like the UIImageView to be shown when there is something in the queue. When every operation in the queue completes, I want to remove the UIImageView.
I thought that this is something really easy to do, but I've been stuck now for the past hour. Showing the UIImageView is really easy, but I can't seem to remove it. It's probably something really simple that I'm just overlooking. Here's my code. Thank you! :)
- (void)viewDidLoad {
//set up the uiimageview
self.spinnerView = [[UIImageView alloc] initWithFrame:CGRectMake([[UIScreen mainScreen] bounds].size.width-44,0,44,44)];
self.spinnerView.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"0.gif"],
[UIImage imageNamed:#"1.gif"],
[UIImage imageNamed:#"2.gif"],
[UIImage imageNamed:#"3.gif"],
[UIImage imageNamed:#"4.gif"], nil];
self.spinnerView.animationDuration = 0.5f;
self.spinnerView.tag = 998;
self.spinnerView.animationRepeatCount = 0;
[self.view addSubview: self.spinnerView];
//set up the queue
self.uploadQueue = [[NSOperationQueue alloc] init];
[self.uploadQueue setMaxConcurrentOperationCount:1];
//set up observer for the queue
[self.uploadQueue addObserver:self forKeyPath:#"operationCount" options:NSKeyValueObservingOptionNew context:NULL];
}
- (void)newUpload:(NSData*)data {
[self.spinnerView startAnimating];
//....
//request is a NSURLRequest that's set up in this method
[NSURLConnection sendAsynchronousRequest:request queue:self.uploadQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
}];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context
{
if (object == self.uploadQueue && [keyPath isEqualToString:#"operationCount"]) {
if (self.uploadQueue.operationCount == 0) {
[self.spinnerView stopAnimating];
}
}
else {
[super observeValueForKeyPath:keyPath ofObject:object
change:change context:context];
}
Am I doing this correctly? Is there a better way to do it? I've been stuck here for a while and am starting to think that perhaps it's not the UIImageView that's messing up, but rather the way that I'm adding NSURLRequests to the NSOperationQueue.
Thanks again!
Why don't you try https://github.com/samvermette/SVProgressHUD or https://github.com/jdg/MBProgressHUD ? They were made exactly for that purpose (showing modal loading window while doing some asynchronous job). And they are both easy to use and easy customizable for your images and many other options.
The documentation for sendAsynchronousRequest:queue:completionHandler: says that an operation is only added to the specified operation queue after the asynchronous URL request has completed. This operation is just a completion block.
So I do't think you are really adding adding operations in the way you intend to your queue. The URL requests will be running on their own threads outside the queue, only the completion blocks are put on the queue. If you haven't specified anything in the completion block itself then perhaps it is not even added to the queue at all?
Either way I don't think you are adding 5 URL operations to the queue, which then execute one after the other with operationCount == 5, 4, 3, 2, 1, 0. You are more likely firing 5 simultaneous URL requests with their completion blocks being added to the queue in an indeterminate sequence after their URL requests happen to finish.
To do what I think you intend to do, you could:
Write a "concurrent" NSOperation subclass that contains and
manages an NSURLConnection and NSURLRequest etc.
Use AFNetworking.
Continue with sendAsynchronousRequest:queue:completionHandler: but use the
completion handler of one operation to start the next request, and
the completion handler of the final request to stop the spinner.
You could just use the main queue here as the only work being done
in the queue is starting the next operation or stoping the spinner. The actual work of the URL Request is done on it's own thread anyway.
Writing your own concurrent NSOperation is a bit tricky, I wrote one myself, but I probably should have just used AFNetworking. Option 3 is probably the quickest if it meets your needs.
Add this to your .h file: UIImageView *spinnerView; In your .m file, you would want something like this in your -(void)newUpload:
if (code that says file uploads are done) {
spinnerView.hidden = YES;
}
I have a method that builds a package, sends it to a web service, gets a package back, opens it and returns me a nsdictionary. How can I call it on a background queue in order to display a HUD while it requests the data?
You could detach a new thread like following
- (void) fetchData
{
//Show Hud
//Start thread
[NSThread detachNewThreadSelector:#selector(getDataThreaded)
toTarget:self
withObject:nil];
}
- (void) getDataThreaded
{
//Start Fetching data
//Hide hud from main UI thread
dispatch_async(dispatch_get_main_queue(), ^{
//Update UI if you have to
//Hide Hud
});
}
Grand central dispatch (gcd) provides great support for doing what you ask. Running something in the background using gcd is simple:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_NORMAL, 0) ^{
NSDictionary* data = [self fetchAndParseData];
dispatch_async(dispatch_get_main_queue(), ^{
[self dataRetrieved:data];
});
});
This call will return immediately (so your UI will continue to be responsive) and dataRetrieved will be called when the data is ready.
Now, depending on how fetchAndParse data works it may need to be more complicated. If you NSURLConnection or something similar, you might need to create an NSRunLoop to process data callbacks on the gcd thread. NSURLConnection for the most part is asynchronous anyway (though callbacks like didReceiveData will be routed through the UI thread) so you can use gcd only to do the parsing of the data when all the data has been retrieved. It depends on how asynchronous you want to be.
In addition to previous replies, why don't you use NSOperation and NSOperationQueue classes? These classes are abstractions under GCD and they are very simple to use.
I like NSOperation class since it allows to modularize code in apps I usually develop.
To set up a NSOperation you could just subclass it like
//.h
#interface MyOperation : NSOperation
#end
//.m
#implementation MyOperation()
// override the main method to perform the operation in a different thread...
- (void)main
{
// long running operation here...
}
Now in the main thread you can provide that operation to a queue like the following:
MyOperation *op = [[MyOperation alloc] initWithDocument:[self document]];
[[self someQueue] addOperation:op];
P.S. You cannot start an async operation in the main method of a NSOperation. When the main finishes, delegates linked with that operations will not be called. To say the the truth you can but this involves to deal with run loop or concurrent behaviour.
Here some links on how to use them.
http://www.cimgf.com/2008/02/16/cocoa-tutorial-nsoperation-and-nsoperationqueue/
https://developer.apple.com/cocoa/managingconcurrency.html
and obviously the class reference for NSOperation