When I use ObjCHiredis redis to connect to a invalid IP and Port, it will blocked.
How to set it to nonblock mode? Thx
- (void)setUp {
NSLog(#"begin setUp");
NSString *ip = #"125.124.125.125";
self.redis = [ObjCHiredis redis:ip on:[NSNumber numberWithInt:123] db:[NSNumber numberWithInt:0]];
if(self.redis == nil)
{
NSLog(#"init redis error.");
return ;
}
NSLog(#"end setUp");
}
The easiest way to do this is probably going to be by running the connection on a separate thread and using a block to handle the response back on the main thread.
This is a helper method I regularly use to do any sort of async tasks.
I've adapted it so it should work for what you're trying do:
+(void)threadedConnect:(NSString*)ip On:(int)port DB:(int)db WithResponseTask:(void(^)(ObjCHiredis *redis))task
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(queue, ^{
ObjCHiredis *redis = [ObjCHiredis redis:ip
on:[NSNumber numberWithInt:port]
db:[NSNumber numberWithInt:db]];
// Run completion block back on main thread with result as argument
dispatch_async(dispatch_get_main_queue(), ^{ task(redis); });
});
}
Related
I am creating a serial queue in which i add two task as shown below
dispatch_queue_t serial = dispatch_queue_create("com.apple.serial", DISPATCH_QUEUE_SERIAL);
**//Task 1**
dispatch_async(serial, ^{
[NMUserAPIManager getUserProfileData:^(NMUser *objUser) {
NSLog(#"Get User Profile .....");
_objUser = objUser;
}];
});
**//Task 2**
dispatch_async(serial, ^{
[NMUserAPIManager getUserRecentTransactionData:^(NSDictionary *responseDictionary) {
_accountTableView.hidden = NO;
[self recentTransactionSetup:responseDictionary];
NSLog(#"Get User Recent transaction");
dispatch_async(dispatch_get_main_queue(), ^{
[self reloadTableData];
});
}];
});
Inside that two task i am calling web service with NSURLSession. Problem is that before my Task 1 completion handle Task2 completion handle get called. According to theory by using serial queue each task waits for the previous task to finish before being executed. It my understanding is correct.
NSURLSession's already run on a background thread, so the issue you are seeing here is that as far as your serial queue is concerned once you call 'getUserProfileData:' technically the work for that block in your queue is finished because the NSURLSession is running on a different thread. If your main goal here is to simply call your second task after your first one completes I don't think you need your own queue you would probably be better off simply doing something like:
[NMUserAPIManager getUserProfileData:^(NMUser *objUser) {
NSLog(#"Get User Profile .....");
_objUser = objUser;
[self getUserTransactions];
}];
-(void)getUserTransactions
{
[NMUserAPIManager getUserRecentTransactionData:^(NSDictionary *responseDictionary) {
_accountTableView.hidden = NO;
[self recentTransactionSetup:responseDictionary];
NSLog(#"Get User Recent transaction");
dispatch_async(dispatch_get_main_queue(), ^{
[self reloadTableData];
});
}];
}
EDIT:
If you are looking for something a little more robust I would check out this post for how you can subclass NSOperation to make your own Asynchronous Operation which you can then use with an NSOperationQueue.
In my iOS application I am using Core Data.
For table View listing I use NSFetchedResultsController and
Connecting to Remote store I use NSIncrementalStore.
My FetchedResultsController Context is having MainQueue Cuncurrency type.(I couldn't do it with a PrivateQueueCurrencyTYpe).
For resolving Fault, for a many relationship, the executeFetchResultsCall:withContext:error method is executed from my IncrementalStore subclass.
Inside the executeFetchResults method, I will invoke the API (connecting to remote server) if it is not available in my local database.
myarray = [object representationsForRelationship:#"manyconnection" withParams:nil];
Now I need the results array in return synchronously to be returned to the ExecuteFetchResultsMethod. Also this operation should be executed on Main thread.
So I am having only one option to fetch the results from server which causes the UI to unresponsive for the specified sleep time.
-(RequestResult*)makeSyncJsonRequest{
__block RequestResult *retResult = [[RequestResult alloc] init];
__block BOOL block = YES;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
void (^resultBlock)(RequestResult*) = ^(RequestResult* result){
if(!retResult.error)
retResult = result;
block = NO;
dispatch_group_leave(group);
};
// Add a task to the group
dispatch_group_async(group, queue, ^{
// Some asynchronous work
dispatch_group_enter(group);
[self makeAsyncJsonRequestWithBlock:resultBlock];
});
// Do some other work while the tasks execute.
// When you cannot make any more forward progress,
// wait on the group to block the current thread.
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
return retResult;
}
As the above operation is being executed on main thread,the UI hangs.
Inorder to make the UI smoother, I need to carry out the executeFetchrequest in some other thread which is not possible.
It also expects the results array in return.
Is there any option to carry out this something like in a completion handler manner?
or
Any alternate methods or design to work this proper.
Any Help is highly appreciated.
This is a skeleton, using a dispatch_group, assuming you are using an NSFetchedResultsController to update your UITableView:
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// do your setup (FetchedResultsController and such)
[self syncData];
}
- (void)syncData
{
NSArray<Entity*> *results = [self fetchData];
BOOL needsUpdateFromServer = YES; // check your results and set this bool accordingly
if (!needsUpdateFromServer) {
return;
}
__block ServerResponse *fromServer = nil;
__block dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[self loadDataFromServer:^(ServerResponse *response) {
fromServer = response;
dispatch_group_leave(group);
}];
dispatch_group_notify(group,dispatch_get_main_queue(),^{
[self persistData:fromServer];
/*
According to our discussion, you are using an NSFetchedResultsController.
So your tableView should update automatically after persisting the data.
*/
});
}
- (void)loadDataFromServer:(void (^)(ServerResponse *response))completion
{
// [someDownloadService downloadDataFromServerInBackgroundWithCompletion:^(ServerResponse* response){
dispatch_async(dispatch_get_main_queue(), ^{
completion(response);
});
// }];
}
- (NSArray<Entity*>*)fetchData
{
NSArray<Entity*> *results = nil;
// fetch from core data and return it
return results;
}
- (void)persistData:(NSArray<ServerResponse*> *)serverResponses
{
// parse whatever you get from server
// ... and persist it using Core Data
}
#end
If I dispatch_async a block on main queue like this:
-(void) myTask {
dispatch_async(dispatch_get_main_queue(), ^{
[self.service fetchData];
});
}
In unit test, I can execute the block passed in main queue by manually run the main loop like this:
-(void)testMyTask{
// call function under test
[myObj myTask];
// run the main loop manually!
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
// now I can verify the function 'fetchData' in block is called
...
}
Now, I have another similar function which dispatch block to an sequential queue other than main queue:
-(void) myTask2 {
dispatch_async(dispatch_queue_create("my.sequential.queue", NULL), ^{
[self.service fetchData];
});
}
In unit test, how can I execute the block manually now?
-(void)testMyTask2{
// call function under test
[myObj myTask2];
// How to manually execute the block now?
}
=== Clarify ===
The reason why I want to manually execute is because I don't like any Wait-For-Timeout way doing the test. Because waiting time is depending on CPU speed, on different machines could be different. I'd like to manually execute the block passed to queue (the same way as how I did for the main queue test case) and then verify the result.
You could create the queue in your test function.
-(void) myTask2:(dispatch_queue_t*)queue {
dispatch_async(*queue, ^{
[self.service fetchData];
});
}
-(void)testMyTask2{
dispatch_queue_t queue = dispatch_queue_create("my.sequential.queue", NULL);
[myObj myTask2:&queue];
dispatch_sync(queue, ^{
});
}
(Just realised currentRunLoop is not needed)
For execute test in async block use XCTestExpectation class
-(void) myTask2 {
XCTestExpectation *expectation = [self expectationWithDescription:#"catch is called"];
dispatch_async(dispatch_queue_create("my.sequetial.queue", NULL), ^{
[self.serviceClient fetchDataForUserId:self.userId];
[expectation fulfill];
});
[self waitForExpectationsWithTimeout:Timeout handler:^(NSError *error) {
//check that your NSError nil or not
}];
}
Hope this help
I have two methods which run on a serial queue. Each method return a copy of some class. I'm trying to achieve thread safety solution while also mainting data integrity.
for example:
-(Users *) getAllUsers
{
__block copiedUsers;
dispatch_sync(_backgroundQueue, ^{
copiedUsers = [self.users copy]; // return copy object to calling thread.
});
return copiedUsers;
}
-(Orders *) getAllOrders
{
__block copiedOrders;
dispatch_sync(_backgroundQueue, ^{
copiedOrders = [self.Orders copy]; // return copy object to calling thread.
});
return copiedOrders;
}
In addition to this two methods, I have a worker class that add/remove users and orders, all done via a serial queue backgroundQueue.
If in the main thread I call getAllUsers and then getAllOrders right after the other my data integrity isn't safe because between the two calls the worker class might have changed the model.
my question is how can I make to the caller a nice interface that allows multiple methods to run atomically?
Model is only updated from backgroundQueue serial queue.
Client talks to model via a method that receives a block that runs in the background queue.
In addition, not to freeze main thread, I create another queue and run a block that talks with the gateway method.
P.S - attention that dispatch_sync is called only in runBlockAndGetNeededDataSafely to avoid deadlocks.
Code sample:
aViewController.m
ManagerClass *m = [ManagerClass new];
dispatch_queue_t q = dispatch_queue_create("funnelQueue", DISPATCH_QUEUE_SERIAL);
dispatch_block_t block_q = ^{
__Users *users;
__Orders *orders;
[manager runBlockAndGetNeededDataSafely:^
{
users = [manager getUsers];
orders = [manager getOrders];
dispatch_async(dispatch_get_main_queue(),
^{
// got data safely - no thread issues, copied objects. update UI!
[self refreshViewWithUsers:users
orders:orders];
});
}];
}
dispatch_async(q, block_q);
Manager.m implementation:
-(void) runBlockInBackground:(dispatch_block_t) block
{
dispatch_sync(self.backgroundQueue, block);
}
-(Users *) getAllUsers
{
return [self.users copy];
}
-(Orders *) getAllOrders
{
return [self.Orders copy];
}
To answer your question about how to checking the current queue:
First when you create the queue, give it a tag:
static void* queueTag = &queueTag;
dispatch_queue_t queue = dispatch_queue_create("a queue", 0);
dispatch_queue_set_specific(queue, queueTag, queueTag, NULL);
and then run a block like this:
-(void)runBlock:(void(^)()) block
{
if (dispatch_get_specific(queueTag) != NULL) {
block();
}else {
dispatch_async(self.queue, block);
}
}
Your example doesn't work. I suggest to use completion callback. You should have an option to know when the worker finish his job to return to value.
- (void)waitForCompletion:(BOOL*)conditions length:(int)len timeOut:(NSInteger)timeoutSecs {
NSDate *timeoutDate = [NSDate dateWithTimeIntervalSinceNow:timeoutSecs];
BOOL done = YES;
for (int i = 0; i < len; i++) {
done = done & *(conditions+i);
}
do {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:timeoutDate];
if([timeoutDate timeIntervalSinceNow] < 0.0)
break;
//update done
done = YES;
for (int i = 0; i < len; i++) {
done = done & *(conditions+i);
}
} while (!done);
}
-(void) getAllUsers:(void(^)(User* user, NSError* error))completion
{
dispatch_async(_backgroundQueue, ^{
BOOL condition[2] = [self.userCondition, self.orderCondition];
[self waitForCompletion: &condition[0] length:2 timeOut:60];
if (completion) {
completion([self.users copy], nil);
}
});
}
I am looking for a small scenario that how can we trace the "dispatch_async" is running or not?.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
(unsigned long)NULL), ^(void) {
//back ground process
});
In my case, my app will be in foreground I started the back ground thread and when I bring app from background to foreground I need to check whether it is still running or not. I should not call the same process if it is still running. any idea?
The easiest way to do this (without keeping a reference to every dispatch or a flag for entering/leaving asynchronous tasks) is by using dispatch_group notifications. See the example link and code below:
- (void)downloadPhotosWithCompletionBlock:(BatchPhotoDownloadingCompletionBlock)completionBlock
{
// 1
__block NSError *error;
dispatch_group_t downloadGroup = dispatch_group_create();
for (NSInteger i = 0; i < 3; i++) {
NSURL *url;
switch (i) {
case 0:
url = [NSURL URLWithString:kOverlyAttachedGirlfriendURLString];
break;
case 1:
url = [NSURL URLWithString:kSuccessKidURLString];
break;
case 2:
url = [NSURL URLWithString:kLotsOfFacesURLString];
break;
default:
break;
}
dispatch_group_enter(downloadGroup); // 2
Photo *photo = [[Photo alloc] initwithURL:url
withCompletionBlock:^(UIImage *image, NSError *_error) {
if (_error) {
error = _error;
}
dispatch_group_leave(downloadGroup); // 3
}];
[[PhotoManager sharedManager] addPhoto:photo];
}
dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{ // 4
if (completionBlock) {
completionBlock(error);
}
});
}
Note how:
dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{ // 4
if (completionBlock) {
completionBlock(error);
}
});
will not be called until after
dispatch_group_leave(downloadGroup); // 3
is called.
You should setup your threading to where you can work with callbacks like this to determine states. You should try to avoid using boolean flags at all costs, as this is exactly what dispatch groups are for. It's also hard to keep track of numerous asynchronous calls using boolean states.
link: dispatch groups
The question is wrong - dispatch_async is running while you call it and stops running when the call returns, which is practically immediately. What you really want to know is whether the dispatched block is running or not. The simplest way is something along the lines of
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
(unsigned long)NULL), ^(void) {
[self blockIsRunning:YES];
// do stuff
[self blockIsRunning:NO];
});
or if you want to know whether the block has run once, you would do something like
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
(unsigned long)NULL), ^(void) {
[self blockStarted];
// do stuff
[self blockFinished];
});
Alternatively, use NSOperationQueue and a subclass of NSOperation so instead of an anonymous block you have a proper object that you can ask whether it is ready, cancelled, executing, or finished.