Socket Rocket Asynchronous delegate methods - ios

Using Socket Rocket library for WebSocket connections.
When I saw the source of Socket Rocket, it is clearly visible that it is calling the methods of its delegate class (my class) asynchronously using dispatch_async.
But when in my class where the delegate the method is implemented, I put a infinite while loop, the UI gets blocked.
Why is that so when the delegate method is already called asynchronously?
SRWebsocket.m
- (void)_handleMessage:(id)message
{
SRFastLog(#"Received message");
[self _performDelegateBlock:^{
[self.delegate webSocket:self didReceiveMessage:message];
}];
}
// Calls block on delegate queue
- (void)_performDelegateBlock:(dispatch_block_t)block;
{
if (_delegateOperationQueue) {
[_delegateOperationQueue addOperationWithBlock:block];
} else {
assert(_delegateDispatchQueue);
dispatch_async(_delegateDispatchQueue, block);
}
}
My delegate implementation (code that should have been handled asynchronously)
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message {
//some code to process message
//emulating a long running operation
while(1)
{
}
}
But when I put a dispatch_async in my long running operation, the UI doesnt get blocked.
Is it because the block as a whole is run asynchronously but the delegate call inside it is done synchronously. So, as soon as the delegate call is over, _performDelegateBlock returns?
Please explain

_delegateDispatchQueue - default value will your main queue. So, if you want to run code asynchronously for your delegate, you need to provide your own queue

Related

Multiple async methods, one completion handler

I have two async methods:
- (void)login: (void (^)(BOOL))completion{
// some network stuff with completion
}
and
- (void)download: (void (^)(BOOL))completion{
// some network stuff with completion
}
For a background fetch, I need both methods to be completed, and return just one completion.
I could of course, nest the methods, as such
- (void)login: (void (^)(BOOL))completion{
//....
[self download:^(BOOL success) {
//....
if (success){
completion(true);
}
}];
}
However, as soon as I add one or more methods that need to be completed at the same time, code gets messy.
I remember having read about a way, with which you could just kind of group all those methods on one thread, but I just don't seem to have the right vocabulary to find that here on SO or elsewhere.
I hope you understand what I'm talking about, and point me in the right direction.
thanks ahead
If the task really presents a dependency and it's possible that you have to add more methods in the future maybe you should consider use NSOperationQueue.
What you can do is pass the completion block down to the download method so when download completes the completion is chained all the way back up the caller like so:
- (void)login: (void (^)(BOOL))completion{
// some network stuff with completion
[self download:completion];
}
- (void)download: (void (^)(BOOL))completion{
completion(YES);
// some network stuff with completion
}

how to run methods in one specific thread (not main thread)

I call a heartBeats method per 10ms in a specific thread(not main thread), how to call another method at any time in this same thread?
I subclass NSThread like this
#implementation MyThread
{
NSTimeInterval _lastTimeInterval;
}
- (void)main
{
while (true) {
NSTimeInterval timeInterval = [[NSDate date] timeIntervalSince1970]*1000;
if (timeInterval - _lastTimeInterval > 10)
{
[self heartBeats];
_lastTimeInterval = timeInterval;
}
}
}
- (void)heartBeats
{
NSLog(#"heart beats thread: %#", [NSThread currentThread].description);
}
#end
and run it like this
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"main thread: %#", [NSThread currentThread].description);
MyThread *myThread = [[MyThread alloc]init];
[myThread start];
}
- (void)someMethod
{
// do somthing
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
#end
Now,here is the question, how to run - (void)someMethod in myThread?
The main method of your NSThread subclass is everything that runs on that thread. You cannot interrupt it to run other code without the main method's cooperation.
What you really should do is throw out that entire loop and replace it with NSRunLoop and NSTimer.
NSRunLoop keeps the thread alive as long as there's something it will need to do, but also sleeps the thread until it needs to do something.
NSTimer does something on a repeating interval as long as it's scheduled on a run loop.
You need your thread to do two things:
send the MyThread object a heartBeats message (you're doing this)
send the view controller a someMethod message (this is what you asked about)
For the latter, you need one additional thing: A run loop source.
So, clear out your main method and replace it with the following:
Get the current NSRunLoop and store it in a local variable.
Create an NSTimer with a 10-second interval, whose target is self and selector is heartBeats. (Slightly cleaner version: Have another method that takes an NSTimer *but ignores it, so your timer calls that method and that method calls heartBeats. The proper way to set up a timer is to give it a method that expects to be called with a timer, but, in practice, giving it a method that takes no arguments works, too.)
If you didn't create the timer using scheduledTimerWith…, add it to the run loop. (The scheduledTimerWith… methods do this for you.)
Create a run loop source and add it to the run loop.
Call [myRunLoop run].
Step 4 bears explaining:
Core Foundation (but not Foundation; I don't know why) has something called “run loop sources”, which are custom objects that can be added to a run loop and will call something once signaled.
Sounds like what you want, to call your view controller method!
First, in the view controller, change myThread from a local variable in viewDidLoad to an instance variable. Rename it _myThread to make that clear.
Next, add a delegate property to MyThread. This should be weak and have type id <MyThreadDelegate>. You'll also need to define a MyThreadDelegate protocol, which should have one method taking no arguments and returning nothing (void).
You should now be able to set _myThread.delegate = self from the view controller, and implement in the view controller the method that you declared in the protocol. The view controller is now the delegate of its MyThread.
In -[MyThread main], create a version-0 CFRunLoopSource. The Create function takes a “context” structure, containing, among other things, the version (0), an “info” pointer (set this to self, i.e., the MyThread) and a Perform callback (a function, which will be called with the info pointer as its only argument).
In your perform callback, you'll need to do something like this:
MyThread *self = (__bridge MyThread *)info;
[self fireDelegateMessage];
In MyThread, implement that fireDelegateMessage method. In there, send self.delegate the message you declared in your protocol.
Next, add a public method to MyThread (i.e., declare it in MyThread.h as well as implementing it in MyThread.m) named something like “requestDelegateMessage”. In this method, call CFRunLoopSourceSignal on the run loop source. (The documentation for that function suggests that you also need to call CFRunLoopWakeUp on the thread's CFRunLoop. Try it without first.)
Lastly, when the view controller wants someMethod to be called on that thread, call [_myThread requestDelegateMessage].
So:
the view controller calls requestDelegateMessage
requestDelegateMessage signals the run loop source (and wakes up the run loop, if that is needed)
the run loop source calls the perform callback on the MyThread's thread
the perform callback calls fireDelegateMessage on the MyThread's thread
fireDelegateMessage calls the view controller's implementation of the delegate method on the MyThread's thread
the view controller calls someMethod on the MyThread's thread

What is the best way to load data inside a Viewcontroller

I'm writing an iOS app that is getting data from a server. I have several ViewControllers. I used to load data for that viewcontroller under the viewDidLoad method
-(void)ViewDidload
{
[self loadData];
}
-(void)loadData
{
//calling to webservice caller class
}
But this reduces the app's performance. What is the best method to load data within a viewcontroller? For webservice callings, I have a seperate class. Within my loadData method I call to that particular method inside the webservice calling class.
This is going to block my UI.
What do you mean with "this reduces the app performance". Your app is lagging when you are calling your webservice? This is not because you are calling that in viewDidLoad this is because you are doing that in the main thread.
To call your webservice you can use:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
// Call here your web service
dispatch_sync(dispatch_get_main_queue(), ^{
// push here the results to your ViewController
});
});
With this simple solution you are downloading data from your webservice in a separate thread. And pushing the data to your ViewController with the mainThread. This code is not freezing your app. However you will have a moment that nothing happens. This is a good moment to use a UIActivityIndicatorVew.
I guess your interface is lagging.
Try this:
-(void)ViewDidload
{
[NSThread detachNewThreadSelector:#selector(loadData) toTarget:self withObject:nil];
}

block callback not being called from a unit test program in IOS

I have the following setup. I have a object called "View" in which I want to unit test a method which contains two dispatch_async calls with in it.
view.m
typedef void (^onTaskCompletion)(); //defining the block
-(void) viewdidLoad
{
onTaskCompletion block = ^{
// callback into the block };
[self test1:block];
}
-(void) test1:(onTaskCompletion) block
{
//do something
dispatch_async(queue, ^{
// dispatch async into serial queue & do something
dispatch_async(dispatch_get_main_queue){
// calling the block
block();
};
};
}
When I run the IOS APP , the block in -(void) viewdidLoad gets called. Works perfectly fine. But the problem I have is this:
in Tests : XCTestCase (.m fie)
#property (retain) View *view;
-(void) testMyCode
{
onTaskCompletion block = ^{
// Never gets called.
};
[view test1:block];
}
When I try to Unit test this method test1(), The block never gets called.
Note: The break point within test1() method inside the dispatch_get_main_queue() never gets hit when running in test mode but does get hit when I just run the app. Any thoughts as to why it works when the app is run normally but not when running unit tests?
The problem you are facing is that the tests continue onwards even though they are not finished. The solution is to stall the runloop until the async test if finished.
You can use this dead-simple open source macro WAIT_WHILE(<expression>, <time_limit>) found here https://github.com/hfossli/AGAsyncTestHelper
- (void)testAsyncBlockCallback
{
__block BOOL jobDone = NO;
[Manager doSomeOperationOnDone:^(id data) {
jobDone = YES;
}];
WAIT_WHILE(!jobDone, 2.0);
}
If you want to be able to unit test asynchronous code, you will need to wrap the dispatch_async in a class that you can mock. This class would have for example:
- (void)executeInBackground:(void(^)())task;
- (void)executeInForeground:(void(^)())task;
Then, during your tests you can mock this class. Instead of actually calling the tasks, collect the tasks when they are called, and manually have them executed in your test (not actually calling asynchronously):
- (void)executeNextBackgroundTask;
- (void)executeNextForegroundTask;
Then you can explicitly test each order of execution.

Parsing JSON data from WebService on a background thread

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

Resources