I'm setting a timer so that after a second passes I reset a value for my keyboard extension. The problem is that I feel like the following call is stalling my UI:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self resetDoubleTapBool];
})
Is there an asynchronous way of doing this, or a better way in general? Thanks!
The dispatch_after() call itself does not block. At (or shortly after) the appointed time, the block will be submitted to the main queue. Submitting it doesn't block the main thread. When the main thread next runs its run loop or is idle within dispatch_main(), it will execute the block.
IF your -resetDoubleTapBool method takes any appreciable amount of time, that can stall your UI. That's just the same as any code that runs on the main thread. It's not specific to dispatch_after() or any other part of GCD.
According to the function documentation:
This function waits until the specified time and then asynchronously adds block to the specified queue.
Related
I've a serial queue and I use that queue to call a performSelectorWithDelay like below
dispatch_async(serialQueue, ^(void) {
[self performSelector:#selector(fetchConfigFromNetwork) withObject:nil afterDelay:rootConfig.waitTime];
});
However, the method fetchConfigFromNetwork never gets called. However, if instead of serialQueue, I use mainQueue - it starts working.
Cannot understand what's happening here and how to fix it?
The explanation why your code doesn't work is in the documentation: https://developer.apple.com/documentation/objectivec/nsobject/1416176-performselector?language=occ
This method registers with the runloop of its current context, and
depends on that runloop being run on a regular basis to perform
correctly. One common context where you might call this method and end
up registering with a runloop that is not automatically run on a
regular basis is when being invoked by a dispatch queue. If you need
this type of functionality when running on a dispatch queue, you
should use dispatch_after and related methods to get the behavior you
want.
I'm assuming you want that method to be called on the serial queue with a delay. The most straight forward (and recommended way) is to use dispatch_after:
__weak typeof(self) wself = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(rootConfig.waitTime * NSEC_PER_SEC)), serialQueue, ^{
[wself fetchConfigFromNetwork];
});
This method sets up a timer to perform the aSelector message on the current thread’s run loop. The timer is configured to run in the default mode (NSDefaultRunLoopMode). When the timer fires, the thread attempts to dequeue the message from the run loop and perform the selector. It succeeds if the run loop is running and in the default mode; otherwise, the timer waits until the run loop is in the default mode.
This is the discussion about the method performSelector:withObject:afterDelay:, I think the block of dispatch_async will execute on a new thread (not main thread), but you would not know which thread it is, so you can not new a runloop and open it and assign it to this thread. because the runloop of thread is close in default except the main thread, the timer will wait forever.
On my opinion, you should use NSThread instead of dispatch_async, and create a runloop for the thread that you use, then specified the mode of runloop with NSDefaultRunLoopMode, if you actually want to cancelPreviousPerformRequestsWithTarget, otherwise use dispatch_after instead of performSelector.
That's my understanding. I can't promise it is right.
I have function ,
-(void)serverFetch{
//server fetch
}
In every 15mintutes, i'm calling this method using NSTimer,
[NSTimer scheduledTimerWithTimeInterval:900.0f repeats:YES block:^(NSTimer * _Nonnull timer) {
[self fetchFromServer];
}];
I'm using APNS in my app, so when we receive the notification , again i'm calling this method.
So Scheduler thread and this notification thread should not happen in parallel. For instance, when scheduler thread is in operation and push notification arrives then push notification thread should wait for scheduler thread.How can i achieve this?Any help appreciated?
One approach would be to use Grand Central Dispatch (GCD). Create a serial queue and add blocks to it for asynchronous execution when your timer fires or notifications arrive, the blocks will be executed strictly one after the other. This will only work correct if the work each block does is completely synchronous, that is when the block returns all its work is complete.
If your blocks contain asynchronous work then you will also need a semaphore. A block should acquire the semaphore when it starts execution and its final asynchronous action should release it. In this way though the block scheduled by the serial queue returns and the queue starts the next block that next block will immediately block waiting to acquire the semaphore until the previous block's last asynchronous action releases it.
If after studying GCD, designing a solution, and implementing it you have a problem ask a new question, show the code you have written, and explain the problem. Someone will undoubtedly help you move forward.
HTH
Suppose I call the method below on the main thread. If some other thread is doing a write on the array at the time the method is called, the dispatch_sync will block. But it is blocking on another queue (not main queue). When this is blocked, what is the state on the main queue (method cannot move forward until the disaptch_sync returns, but is this treated like a asynchronous call on the main queue). For e.g: will the main queue respond to UI events? If yes, what will happen to the state of the method call when dispatch_sync returns when the reaction to user event is happening?
-(id) objectAtIndex:(NSUInteger)index
{
__block id obj;
dispatch_sync(self.dataAccessQ, ^{
obj = self.embeddedArray[index];
});
return obj;
}
It doesn't matter whether it's the main queue or not. Whatever queue is used to call objectAtIndex: will block on the dispatch_sync call until it completes.
If you call this code on the main queue, it blocks like any other queue. During that time, no user events will be processed. The UI will appear locked during that time. When done, the UI will again work as normal.
No, the main queue is akin of blocked, too.
The main queue is bound to the main thread. Every thread can execute one and only one control flow at a time. If any code is executed on the main thread, no other code can be executed.
Therefore waiting for the result of an operation subscripted to another queue will block the waiting thread. (Not a parallel Q!) This is why we have completion handlers.
Instead of returning an object, add a parameter for a completion handler to your method and call that inside the block at the end.
From Main UIViewController I call
if (required) [dataDB function];
next command;
....
....
in the Database UIViewController
- (void) Function {
Display Alert Message for Processing;
performs steps (takes some time)
....
....
Close Alert Message
}
Although one should NOT block code
I need the [dataDB Function] to complete before next command is called;
Yet I need the Alert to start showing before perform steps starts.
Currently Alert appears after [dataDB Function] completes and closes almost immediately.
Please help.
try this code
if (required)
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[dataDB function];
dispatch_async(dispatch_get_main_queue(), ^{
next command;
});
});
}
it will create async task of [dataDB function];and after competition of it, it will perform task on main queue of next command;
You want to use some combination of GCD dispatch queues and the completion block pattern.
You should use dispatch_async() to queue db functions to complete serially on a background queue (a non-concurrent one, so that they are executed in the order you dispatch them). Then you can show your alert on the main thread while you wait for operations to complete on the background queue. You can use a completion block that you pass to one of the background operations to call back to the main thread on completion, or use the dispatch_group_notify() functionality.
You could also consider using an NSOperationQueue, but I find just dispatching blocks to be much easier.
I have an app that uses a connection queue that handles the connections on a background thread. Each connection sends a JSON post, then when it receives a success, saves some objects into coredata.
Once all connections are complete, i call a dispatch_async on the main thread to call a finished method.
However, under very specific conditions of data im sending/saving, I've noticed the dispatch_async block to the main thread never gets called, and the app screen freezes, all execution stops, and the app sits idle with a frozen screen. processing power according to xcode is 0%.
Here is method with the block that fails.
- (void)connectionDidComplete
{
_completeConnections++;
_syncProgress = (float)_completeConnections / (float)_totalConnections;
dispatch_async(mainQueue, ^(void) {
[[NSNotificationCenter defaultCenter] postNotificationName:SyncQueueDidUpdateNotification object:nil];
}); <-- this dispatch works
if (_completeConnections == _totalConnections)
{
// clear unsynced data
NSArray *syncedObjects = [SyncObject completedSyncObjects];
if (syncedObjects.count > 0)
{
for (SyncObject *syncObject in syncedObjects)
{
[syncObject delete];
}
}
//this method saves the current context, then merges this context with the main context right after
[[VS_CoreDataManager sharedManager] saveManagedObjectContextAndWait:managedObjectContext];
// cleanup the thread's context
[[VS_CoreDataManager sharedManager] unRegisterManagedObjectContextForThread:currentThread];
managedObjectContext = nil;
// complete sync
dispatch_async(mainQueue, ^(void) {
[self performSelector:#selector(finishSync) withObject:nil afterDelay:2];
}); <-- this dispatch never gets called
}
}
My suspicion is this problem has something to do with saving the context then merging it. And possibly while that is happening its released in the middle of the merge, causing some weird hang up and the dispatch isn't getting executed. This is just a guess though, and I don't know how to fix it.
Any ideas?
Thanks.
If the block on the main thread is not executed, then it is because of 1 of 2 reasons.
The main thread is blocked; is not processing any events at all. Got a while() loop on the main thread? That'd do it. A lock? There you go.
The main thread is running a modal run loop inside the outer run loop. Asynchronous dispatches to the main event loop -- main thread -- won't be processed in this case.
Set a breakpoint on that dispatch_async() and see what the main thread is doing (at the point of dispatch the main thread is most likely already in the bad state).
DarkDust's suggestion of using dispatch_after() is a good one, but is unlikely to work in that it is almost assuredly the case that your main thread is not processing events when the problem occurs. I.e. fix the problem, then move to dispatch_after() as DarkDust suggests.
If your main thread is busy with modal runloop, then you could try
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, block
});
I believe this is a great discussion. I came across this when I had the following code:
dispatch_synch(dispatch_get_main_queue()){
print("I am here")
}
the print code did not execute as I was dispatching a 'synch' block on the serial main thread which caused a dead lock. print was waiting for the dispatch to finish and dispatch was waiting for print to finish. When you dispatch in the main serial queue then you should use dispatch_async. and i guess if you use a concurrent queue then dispatch synch suits better