Alright I am kind of new to threads so I have this question.
I am trying to get information of friends from Facebook, and I do not want to do that on the main thread. but for some reason when the request is not on the main thread the callback does never get called and I don't know why!
I have an Array with all the ID's from my friends and loop through this array and create an object of my custom class Friend (which gets all the information I need) with every ID.
I add this object to an array.
This friend object makes an request to Facebook and handles the response to get the data I want.
here is the code:
dispatch_async(dispatch_get_global_queue(0, 0), ^(void) {
[self getFBFriendsInfo];
});
-(void)getFBFriendsInfo{
if (friendsInfoArray) {
[friendsInfoArray removeAllObjects];
}
else{
friendsInfoArray =[[NSMutableArray alloc]init];
}
for (int i=0; i<[UDID count]; i++) {
NSString *udid = [UDID objectAtIndex:i];
FriendsInfo *friend =[[FriendsInfo alloc] initWithFacebook:facebook andUdid:udid];
[friendsInfoArray addObject:friend];
[friend release];
}
dispatch_async(dispatch_get_main_queue(), ^(void) {
[delegate friendsInfosAvailable:friendsInfoArray];
});
}
and in my custom class I do this:
[facebook requestWithGraphPath:udid andDelegate:self];
with this the callback's are never called! only if I do the request on the main thread it works:
dispatch_async(dispatch_get_main_queue(), ^(void) {
[facebook requestWithGraphPath:udid andDelegate:self];
});
This is why on a different thread you get no response:
Facebook will use NSURLConnection to perform requests. For the connection to work correctly the calling thread’s run loop must be operating in the default run loop mode (Read NSURLConnection class reference). When you use dispatch_async() there is no run loop operating in the default run loop mode (unless you are on the main dispatch queue, therefore running on the main thread). Hence, I figure the request isn't even sent (You can check that sniffing your network if you wish.).
So, in a nutshell, you should make your request on the main thread; as it is asynchronous, it wont freeze your app. Then, if the processing of the response is too expensive, handle it in the background.
I really hope this helps.
My best.
Related
I am using the new CoreMotion framework to monitor some of the hardware devices. Here is the typical code to do that:
-(void)startAccelerometer{
self.motion.accelerometerUpdateInterval = 1/30.0f;
NSOperationQueue* accelerometerQueue = [[NSOperationQueue alloc] init];
CMAccelerometerHandler accelerometerHandler = ^(CMAccelerometerData *accelerometerData, NSError *error) {
NSLog(#"Accelerometer realtime values");
NSLog(#"x=%f", accelerometerData.acceleration.x);
NSLog(#"y=%f", accelerometerData.acceleration.y);
NSLog(#"z=%f", accelerometerData.acceleration.z);
NSLog(#" ");
};
[self.motion startAccelerometerUpdatesToQueue:accelerometerQueue withHandler:[[accelerometerHandler copy]autorelease]];
}
That works just fine. Now I want to print the values on a UILabel, but since the CoreMotion frameworks has you use blocks, this is not guaranteed to be in the main queue (and in fact isn't for my app). Is it is "wrong" to just run the label's setter on the main queue like this?
-(void)startAccelerometer{
self.motion.accelerometerUpdateInterval = 1/30.0f;
NSOperationQueue* accelerometerQueue = [[NSOperationQueue alloc] init];
CMAccelerometerHandler accelerometerHandler = ^(CMAccelerometerData *accelerometerData, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
self.lblAccelerometer.text = [NSString stringWithFormat:#"Accelerometer:\nx = %f\ny = %f\nz = %f",
accelerometerData.acceleration.x,
accelerometerData.acceleration.y,
accelerometerData.acceleration.z];
});
};
[self.motion startAccelerometerUpdatesToQueue:accelerometerQueue withHandler:[[accelerometerHandler copy]autorelease]];
}
It works just fine and I don't really see any reason why this would be frowned upon. Any thoughts on that?
This is a common method that I use in many projects. UI updates must occur on the main thread.
//Dispatch on background thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//background processing goes here
//Dispatch on main thread
dispatch_async(dispatch_get_main_queue(), ^{
//update UI here
});
});
In your case, your UI updates are occurring on the main thread. So I wouldn't worry about changing anything.
You are missunderstanding the concept of blocks, to put it simple:
Blocks are small pieces of code that can be handled as variables and be executed at a certain time or thread.
All UI updates MUST be performed on the main thread so as long as you do this it will be fine.
Codes can be executed in different threads with different priorities in sync or async mode. On your code you are doing it perfectly fine, you not only dispatch it to the Main Queue which is where uiupdates should be executed, but you are also dispatching it async which is the safest way to update send to the main queue (from your code i cannot tell if you are running this specific piece of code from the main queue or a secondary queue but if u were to dispatch a sync block from the main queue to the main queue your program would stop working)
For iOS documentation:
Use the dispatch_get_main_queue function to get the serial dispatch
queue associated with your application’s main thread. This queue is
created automatically for Cocoa applications and for applications that
either call the dispatch_main function or configure a run loop (using
either the CFRunLoopRef type or an NSRunLoop object) on the main
thread.
Read this here http://developer.apple.com/library/ios/#documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html#//apple_ref/doc/uid/TP40008091-CH102-SW1
I want to return information from a turn based game from the game center servers, which is all fine, but I want the player alias which is acquired using the asynchronous method:
[GKPlayer loadPlayersForIdentifiers:singleOpponentArray withCompletionHandler:^(NSArray *players, NSError *error) {
GKPlayer *returnedPlayer = [players objectAtIndex:0];
NSString *aliasToAdd = [NSString stringWithString:returnedPlayer.alias];
NSString *idToAdd = [NSString stringWithString:returnedPlayer.playerID];
NSDictionary *dictionaryToAddToAliasArray = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:aliasToAdd, idToAdd, nil] forKeys:[NSArray arrayWithObjects:#"alias", #"id", nil]];
[self.aliasArray addObject:dictionaryToAddToAliasArray];
}];
But the UI uses this information and it does't arrive in time. How can I make that method execute synchronously on the main thread?
Thanks.
Any UI related code must execute on the main thread.
If your app must wait for the asynchronous call to return, then first disable the UI. For example, set userInteractionEnabled = NO on your UIView.
Then, when the asynchronous methods returns, re-enable the UIView.
In the meantime, display some sort of activity indicator, e.g. UIActivityIndicatorView.
Of course, only do the above in a case where you can't perform the task in the background. Never needlessly block the UI. I'm sure you know that already of course but it's worth restating for any people new to the platform that might be reading this.
To invoke on the main thread, use one of the variants of NSObject's performSelectorOnMainThread method. Or, alternatively, queue it on gcd using the main queue by calling the dispatch_get_main_queue function.
You can do this using GCD functions:
// Show an UILoadingView, etc
[GKPlayer loadPlayersForIdentifiers:singleOpponentArray
withCompletionHandler:^(NSArray *players, NSError *error) {
// Define a block that will do your thing
void (^doTheThing)(void) = ^(void){
// this block will be run in the main thread....
// Stop the UILoadingView and do your thing here
};
// Check the queue this block is called in
dispatch_queue_t main_q = dispatch_get_main_queue();
dispatch_queue_t cur_q = dispatch_get_current_queue();
if (main_q != cur_q) {
// If current block is not called in the main queue change to it and then do your thing
dispatch_async(main_q, doTheThing);
} else {
// If current block is called in the main queue, simply do your thing
doTheThing();
}
}];
I'm writing a very Simple Chat Application and would like to know how to suspend the long polling selector when the Application enters background.
Currently, I have a Chatroom class (A UIView) which handles the long polling like so:
-(void)startPolling
{
[self performSelectorInBackground:#selector(longPoll) withObject: nil];
}
- (void) longPoll {
//Poll the Requested URL...
NSData* responseData = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response error:&error];
[self performSelectorOnMainThread:#selector(dataReceived:)
withObject:responseData waitUntilDone:YES];
[self performSelectorInBackground:#selector(longPoll) withObject: nil];
}
-(void) dataReceived: (NSData*) data
{
//Reload my Tableview etc..
}
How do I use applicationDidEnterBackground: to suspend the longPoll selector until the application comes back to the foreground? Or is this automatically done by the Application Delegate?
The request will automatically be suspended. It's not guaranteed that the request will necessarily succeed after being resumed, so you'll have to handle errors, but it shouldn't break.
Note that there are probably better ways to write this than using performSelectorInBackground:, which always spins up a new hardware thread. For starters, it's probably better to simply loop inside longPoll instead of starting a new thread for the new request.
one of the central tenets of the architecture of my latest app is that I'm going to call methods on the app's model which will be async and accept failure and success scenario blocks.
i.e., The UI calls the model method with 2 blocks, one for success and one for failure.
This is great because the context of the original call is retained, however, the block itself is called on the background thread. Is there anyway of calling a block on the main thread??
Hopefully I have explianed it ok, if not, basically, my model methods are async, return immediately and create a new thread on which to run the op. Once the op returns I will invoke a block which will postprocess the returned data, THEN i need to call the block for the success scenario defined by the called inside the UI. However, the success and failure scenario blocks defined in the UI should be called in the main thread because I need to interact with UI elements which should only be done on the main thread I believe.
many thanks
Something like this is probably what you're after:
- (void) doSomethingWhichTakesAgesWithArg: (id) theArg
resultHandler: (void (^)(BOOL, id, NSError *)) handler
{
// run in the background, on the default priority queue
dispatch_async( dispatch_get_global_queue(0, 0), ^{
id someVar = [theArg computeSomething];
NSError * anError = nil;
[someVar transmuteSomehowUsing: self error: &anError];
// call the result handler block on the main queue (i.e. main thread)
dispatch_async( dispatch_get_main_queue(), ^{
// running synchronously on the main thread now -- call the handler
handler( (error == nil), theArg, anError );
});
});
}
If you are using GCD, you can use the "get main queue":
dispatch_queue_t dispatch_get_main_queue()
Call this inside an async dispatch. i.e.
dispatch_async(dispatch_get_main_queue(), ^{
/* Do somthing here with UIKit here */
})
The example block above could be running in an async background queue and the example code would send the UI work off to the main thread.
Similar approach works also with NSOperationQueue:
NSBlockOperation *aOperation = [NSBlockOperation blockOperationWithBlock:^
{
if ( status == FAILURE )
{
// Show alert -> make sure it runs on the main thread
[[NSOperationQueue mainQueue] addOperationWithBlock:^
{
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:#"Alert" message:#"Your action failed!" delegate:nil
cancelButtonTitle:#"Ok" otherButtonTitles:nil] autorelease];
[alert show];
}];
}
}];
// myAsyncOperationQueue is created somewhere else
[myAsyncOperationQueue addOperation:aOperation];
NSObject has a method:
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait
Create a method that takes a NSDictionary parameter in a convenient class that will always be around (like your app delegate, or a singleton object), package up the block and its parameters into a NSDictionary or NSArray, and call
[target performSelectorOnMainThread:#selector(doItSelector) withObject:blockAndParameters waitUntilDone:waitOrNot];
I use AsyncSocket on the iPhone to communicate with a server. AsyncSocket is based on run loops but my app is based on threads. That means, I start a new thread to write data and wait until a response is received on the same thread. But I can't call an AsyncSocket's method directly from another thread, I have to use:
[self performSelectorOnMainThread:#selector(writeSomeData:) withObject:dataToWrite waitUntilDone:YES];
It does work, but I cannot get the response from my method "writeSomeData:" called this way, because performSelectorOnMainThread returns nothing.
The method writeSomeData: does something like this:
-(NSData *)writeData:(NSData *)dataToWrite {
dataReceived = nil; // AsyncSocket writes data to this variable
[asyncSocket writeData:dataToWrite withTimeout:-1 tag:0];
[asyncSocket readDataToData:[#"<EOF" dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];
int counter = 0;
while (dataReceived == nil && counter < 5) {
// runLoop is [NSRunLoop currentRunloop]
[runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.3]];
++counter;
}
return [dataReceived copy];
}
I could get the response by accessing the class variable "dataReceived", but it's content is changed at this time.
Can anybody tell me how to use AsyncSocket (or generally, how to deal with run loop based classes) on separate threads, so that if I call a method of that class it blocks until the method is executed and a response is received?
Thank you.
Try using GCD(Grand Central Dispatch) to write your data on a separate thread and than come back to the main thread the moment that the data was written. You could do it like this:
// call this on the main thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSData *data = [self writeData:dataToWrite];
dispatch_async(dispatch_get_main_queue(), ^{
// do something with the data on the main thread.
});
});
I hope something like this can help you...