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...
Related
I'm creating a REST client class for my iPad app. So I created a BOOL method which does the login using an NSURLConnection subclass I created earlier.
This JWURLConnection has block type properties for the finishLoading and failWithError operations.
The Problem is that the URL connection most likely finishes (or fails) AFTER this method is completely executed. A cannot use an extra method to use performSelector:waitUntilDone: too because I have to wait for the connection.
Now I tried using plain C semaphores and an extra thread (so that the semaphore blocks only the RESTClient thread, not the URLConnections one), but I had no success; the method started waiting but the whole connection stuff was frozen, thus there where no NSLogs from the connection.
The JWURLConnection starts it's own thread by itself within the -start method:
- (void)start { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [super start]; }); }
Here is the code I tried it with (using semaphores):
- (BOOL)loginWithUsername:(NSString *)uName ansPassword:(NSString *)pWord {
__block BOOL loginSucceeded = NO;
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
JWURLConnection *connection = [JWURLConnection connectionWithPOSTRequestToURL:POSTData:];
[connection setFinished^(NSData *data) {
// validate server response and set login variable
loginSucceeded = YES;
dispatch_semaphore_signal(sema);
}];
[connection setFailed:^(NSError *error) {
loginSucceeded = NO;
NSLog(#"Login failed: %#", [error description]);
dispatch_semaphore_signal(sema);
}];
[connection start];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
// do some more stuff like error handling / reporting here
return loginSucceeded;
}
I hope you can lead my the right direction...
The JWURLConnection starts it's own thread by itself within the -start method:
- (void)start { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [super start]; }); }
You need to ensure that a NSURLConnection's delegate methods will be scheduled on a NSRunLoop or a NSOperationQueue. While the start method could actually take care of this - the given code and your comment indicate it does not ;) In short, dispatch_async does not guarantee that the underlaying thread has a run loop and a dispatch queue does not even guarantee that the underlaying thread is always the same.
The docs show how to schedule a connection.
I would suggest to schedule the connection on the main thread, and change this to a NSOperationQueue when required.
Your loginWithUsername:andPassword: method will simply return immediately since you call/invoke an asynchronous function/method.
Employing asynchronous patterns is kinda "infectious". Once you started using asynchronous programming style, you cant get "rid of" it unless you use synchronization primitives that block the current thread. I would suggest to keep the async style:
- (void) loginWithUsername:(NSString *)uName
andPassword:(NSString *)pWord
completion:(void(^)(id result))onCompletion;
And later:
[self loginWithUsername:#"Me" andPassword:#"secret" completion:^(id result) {
if ([result != [isKindOfError class]]) {
[self fetchImagesWithURL:url completion: ^(id result) {
...
}];
}
}];
I need to make a call getValuesAndCalculate in my app, which should return only after doing its work. However, for doing its work, it needs to get records from a server, which has to be done through an async call. The server data is received through a callback function. Thus, in getValuesAndCalculate I need to wait till I have the data before proceeding with the calculations. How do I implement this?
Use protocols and delegates :
delegates are nothing but you assigning an object from your class to a object on the server side .
The server can use this object to call a method on your client side code.
Try using NSRunloop till you get the data from the server.
For eg :
while (!isFinished)
{
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:1.0]
}
You can implement Threads for this question i.e,
// in main thread
// start activity indicator
NSThread *calculateThread = [[NSThread alloc] initWithTarget:self selector:#selector(getValuesAndCalculate) object:nil];
[calculateThread start];
// End of Main thread
- (void)getValuesAndCalculate
{
// Perform transactions with server
[self performSelectorOnMainThread:#selector(continueMainThreadOperations) withObject:nil waitUntilDone:YES];
}
Thats it!
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.
I've a multi-threading application in which each thread has to do some job, but at a certain point some code needs to be executed serially (like writing into sqlite3 database), so I'm calling that code to be performed on main thread using:
[self performSelectorOnMainThread:#selector(serialJob:) withObject:object waitUntilDone:YES];
and every thing went just fine except that when that code needs some time the user interaction with the application gets disabled until that code has been finished, so is there any way to make another ONE thread that can be run on background and can be called whenever I need it just like the main one so I can replace the previous call with:
[self performSelector:#selector(serialJob:) onThread:REQUIRED_THREAD withObject:object waitUntilDone:YES];
this thread should be some class's static data member to be accessed from all over the code.
any help would be very appreciated, and many thanks in advance...
This is quite easy to do, just spawn your thread and let it run it's runloop using [[NSRunLoop currentRunLoop] run]. That's all that is required to be able to use performSelector:onThread: with a custom thread.
If you are on iOS 4 or newer you should consider using Grand Central Dispatch queues instead of threads though. The GCD APIs are much easier to use and can utilize the system resources much better.
Like Sven mentioned, look into Grand Central Dispatch.
You can create a queue like this:
dispatch_queue_t myQueue = dispatch_queue_create("com.yourcompany.myDataQueue", NULL);
Now you can call blocks on that queue:
dispatch_async(myQueue, ^{
// Your code to write to DB.
});
When you're done, don't forget to release the queue:
dispatch_release(myQueue);
Due to the my question that I need the current thread to be blocked until the database job has been finished, I've tried these two solutions and they worked perfectly. You can either use critical sections or NSOperationQueue and I prefer the first one, here is the code for both of them:
define some class "DatabaseController" and add this code to its implementation:
static NSString * DatabaseLock = nil;
+ (void)initialize {
[super initialize];
DatabaseLock = [[NSString alloc] initWithString:#"Database-Lock"];
}
+ (NSString *)databaseLock {
return DatabaseLock;
}
- (void)writeToDatabase1 {
#synchronized ([DatabaseController databaseLock]) {
// Code that writes to an sqlite3 database goes here...
}
}
- (void)writeToDatabase2 {
#synchronized ([DatabaseController databaseLock]) {
// Code that writes to an sqlite3 database goes here...
}
}
OR to use the NSOperationQueue you can use:
static NSOperationQueue * DatabaseQueue = nil;
+ (void)initialize {
[super initialize];
DatabaseQueue = [[NSOperationQueue alloc] init];
[DatabaseQueue setMaxConcurrentOperationCount:1];
}
+ (NSOperationQueue *)databaseQueue {
return DatabaseQueue;
}
- (void)writeToDatabase {
NSInvocationOperation * operation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(FUNCTION_THAT_WRITES_TO_DATABASE) object:nil];
[operation setQueuePriority:NSOperationQueuePriorityHigh];
[[DatabaseController databaseQueue] addOperations:[NSArray arrayWithObject:operation] waitUntilFinished:YES];
[operation release];
}
these two solutions block the current thread until the writing to database is finished which you may consider in most of the cases.
I need to guarantee that the same thread performs various actions at arbitrary times. First the thread needs to initialize a library, then I want the thread to sleep until work needs to be done and upon user input, I need to be able to pass selectors or blocks for execution.
How can I setup an NSRunLoop to sleep after initialization? After which, how do I signal the run loop to wake up and do something?
I've tried reading the Threading Programming Guide for iOS, but I'd like to avoid setting up classes as custom input classes and use something more lightweight like performSelector:onThread:
Can I set a timer to fire forever from now so the run loop doesn't end?
Here's essentially what I want in pseudo-code:
// Initialization Code...
do {
sleepUntilSignaled();
doWorkSentToThisThread();
while (!done);
Where I send the work to do as a performSelector:onThread: message. It would be even better if I could send the run loop a block like: ^{[someObj message]; [otherObj otherMsg];} but I'd be happy with performSelector since I'm pretty sure that's possible without much extra coding.
Thanks!
You have all the necessary pieces together in your question. You start your thread and have it run it’s runloop. If you need the thread to do something you can use performSelector:onThread: on the main thread to do it.
There is one thing with the runloop you have to be aware though: It won’t run unless it has an input source or a timer attached to it. Just attach a timer to the run loop that fires some time in the distant future and you’re all set.
// Initialization code here
[NSTimer scheduledTimerWithTimeInterval: FLT_MAX
target: self selector: #selector(doNothing:)
userInfo: nil repeats:YES];
NSRunLoop *rl = [NSRunLoop currentRunLoop];
do {
[rl runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
} while (!done);
Using performSelector:onThread:withObject: you can also pass your block to the background thread. All you need to do is to write a method somewhere that takes an block as a parameter and runs it:
#interface NSThread (sendBlockToBackground)
- (void) performBlock: (void (^)())block;
#end
#implementation NSThread (sendBlockToBackground)
- (void) performBlock: (void (^)())block;
{
[self performSelector: #selector(runBlock:)
onThread: self withObject: block waitUntilDone: NO];
}
- (void) runBlock: (void (^)())block;
{
block();
}
#end
But maybe you should use a dispatch queue instead of all this. This requires less code and probably has less overhead also:
dispatch_queue_t myQueue = dispatch_queue_create( "net.example.product.queue", NULL );
dispatch_async( myQueue, ^{
// Initialization code here
} );
// Submit block:
dispatch_async( myQueue, ^{
[someObject someMethod: someParameter];
} );
A dispatch queue created using dispatch_queue_create is a serial queue - all blocks sent to it will be performed in the same order they arrived, one after another.
Consider using NSConditionLock. It is designed for tasks like this. Imagine that you have a queue with data. First thread adds data to queue, second thread waits for data and processes it.
id condLock = [[NSConditionLock alloc] initWithCondition:NO_DATA];
//First thread
while(true)
{
[condLock lock];
/* Add data to the queue. */
[condLock unlockWithCondition:HAS_DATA];
}
//Second thread
while (true)
{
[condLock lockWhenCondition:HAS_DATA];
/* Remove data from the queue. */
[condLock unlockWithCondition:(isEmpty ? NO_DATA : HAS_DATA)];
// Process the data locally.
}
I think you can use NSInvocationOperation with NSOperationQueue.