Eventually I have used asyncAfter feature in swift with no time, and it does some delay and seems different with normal code.
Code with async:
DispatchQueue.main.asyncAfter(deadline: .now() + 0.0 , execute: {
self.updateUI()
})
Normal code:
self.updateUI()
Similar problem with perform selector also.
self.performSelector("onFlip", withObject: nil, afterDelay: 0)
Is that delay is caused because of creating new thread?
This is expected.
DispatchQueue.main.asyncAfter(deadline: .now() + 0.0 , execute: {
self.updateUI()
})
Is essentially the same as
DispatchQueue.main.async {
self.updateUI()
}
which also won't execute immediately due to the fact that you dispatch the code to the main thread asynchronously. Due to the async dispatch, the code inside the closure won't be executed immediately, only on the next runloop of main, which might result in a measurable delay .
asyncAfter doesn't create a new thread, since you are dispatching on the main queue, which will use the main thread, but neither will it execute the dispatched code immediately.
You have submitted a block of code to run on the main queue, so some time will elapse before the runloop can execute that code; particularly if the asyncAfter is being called on the main thread.
Related
I'd like to create a function that performs multiple background operations but the caller should not be aware of its asynchronous nature. So when a caller calls that function it should block the caller's thread and continue after it finishes all the tasks.
Ideally, the function should be called by just invoking its name (say blockingFunction()).
How do I achieve that?
(The main thread isn't a concern here)
We will posit the following test method:
func test() {
print("start")
self.doYourThing()
print("finish")
}
That function is internally synchronous: it proceeds one line at a time from start to finish.
We also have an asynchronous method using an old-fashioned completion handler:
func behaveAsynchronously(completion: #escaping () -> ()) {
DispatchQueue.global().asyncAfter(deadline: .now()+10) {
completion()
}
}
We will consider the problem solved if doYourThing somehow calls behaveAsynchronously and yet "finish" prints 10 seconds after "start". Ready? Here we go:
func doYourThing() {
let group = DispatchGroup()
group.enter()
self.behaveAsynchronously {
group.leave()
}
group.wait()
}
QED.
Note that we are blocking the main thread for 10 seconds, which is illegal; if you did that in real life, you'd crash. Also, there must be multiple threads in the story, or we would be attempting to wait on the same thread we are delayed on and a deadlock results.
I have implemented following completion block, one block is completed and then I update UI and object accordingly.
func doPaging() {
fetchProducts(page: pageNumber , completion: { success in
if let products = success as? Products
{
DispatchQueue.main.async {
self.products.append(contentsOf:products)
self.isWating = false;
self.productTableView.reloadData()
}
}
})
}
func fetchProducts(page: Int, completion: #escaping ((AnyObject) -> Void)) {
// URLSession call here
}
However, the following approach clearly shows restful call will happen in background thread and once it is completed, then update UI and objects.
func doPaging() {
DispatchQueue.global(qos: .background).async {
// Background Thread
fetchProducts()
DispatchQueue.main.async {
self.pageNumber += 1
self.productTableView.reloadData()
self.isWating = false
}
}
}
func fetchProducts(page: Int) {
// URLSession call here
}
I am confused between completion block method vs. DispatchQueue.
Which one is recommended?
In the first approach, you call a method fetchProducts() which internally uses NSURLSession. REST call using NSURLSession runs in background and on completion of the REST call, the completion of the task will be called. In that completion, you call your completion handler of fetchProducts(). This approach seems fine to me.
In the second approach, you use global background queue and asynchronously call NSURLSession APIs (I assume so), and don’t wait for the call to complete. The code on main queue will be instantly called and at this point the NSURLSession task may or may not have been completed.
So, this approach is problematic.
First method seems OK as long as you fetchProducts asynchronously. In fetchProducts() , if you call the completion block in the main queue you won't even need to get main queue again in the doPaging() method.
In your second method, you are calling fetchProducts() in a global (concurrent) queue. Although global queues start each task in the order they were added to queue, they run tasks concurrently. And since fechtProduct() takes time, your code block that contains self.pageNumber += 1 executed before even fetchProduct's URLSession is started. So, this approach won't work.
Completion block and Dispatch Queue are two different concepts.
Completion block is used when your function perform actions takes time to run, and need to return back and run some code even the functions has "ended". For example,
func networkCall(foo: Int, completion:#escaping (_ result:Bool)-> Void))
func otherFunc(){...}
func A(){
networkCall(foo:1){ (success) in
// handle your stuff
}
otherFunc()
}
When you run A(), it first run networkCall(), however networkCall() may takes time to run the network request and the app moved on to run otherFunc(). When the network request is done, networkCall() can call it's completion block so that A() can handle it again.
Dispatch Queue is the threading stuff safely encapsulated by Apple. Network request can be performed in Main thread as well, but it will be blocking other functions.
A common practice is to call Network request in background queue
DispatchQueue.global(qos: .background).async and call completion block after finished. If anything needs to be updated in main thread like UI, do it in the DispatchQueue.main.async
I have a method which gives me photo auth status.
func photosAuthorizationStatus() -> PHAuthorizationStatus {
var authStatus = PHAuthorizationStatus.notDetermined
let semaphore = DispatchSemaphore(value: 0)
PHPhotoLibrary.requestAuthorization { (status: PHAuthorizationStatus) in
authStatus = status
semaphore.signal()
}
semaphore.wait()
return authStatus
}
I am calling this method in viewDidAppear of a ViewController , but Application is not freezing.
But if I call semaphore.wait when I ask mainQueue explicitly Application is freezing.
DispatchQueue.main.async{
let semaphore = DispatchSemaphore(value: 0)
semaphore.wait()
}
// Above code will freeze the application.
Can I know the reason ?
In your title, you ask:
can I call on semaphore.wait() main thread?
You should avoid blocking the main thread for any reason. Whether wait for semaphores or dispatch groups, or even synchronously dispatching (e.g., sync) of anything more than a few milliseconds. You risk having the watchdog process kill your app if you do this at the wrong time, and it results in a horrible UX.
You then go on to ask:
DispatchQueue.main.async {
let semaphore = DispatchSemaphore(value: 0)
semaphore.wait()
}
Above code will freeze the application.
Can I know the reason
That code says “block the main thread waiting for a signal on this semaphore”. So, until that signal arrives, the main thread will be blocked. But the main thread should never be blocked because it services, amongst other things, the UI, and your app will freeze if you deadlock the main thread.
Bottom line, never block the main thread.
Create completion closure in method which will call after successful request authorisation completed. See following code.
Make sure your have added permission key "Privacy - Photo Library Usage Description" in Info.plist file.
func photosAuthorizationStatus(completion: #escaping (PHAuthorizationStatus) -> Void) {
PHPhotoLibrary.requestAuthorization { (status: PHAuthorizationStatus) in
completion(status)
}
}
Use:
self.photosAuthorizationStatus { (status) in
// use your status here
}
Output:
A bit late to the party, but it may be useful for others, since nobody explained the real issue here.
Calling semaphore.wait() decreases the counting semaphore. If the counter becomes smaller than zero, wait() blocks the main queue until you signal the semaphore.
Now, you invoke semaphore.signal() in the completion closure, which happens to execute on the main queue. But the main queue is blocked, so it won't call semaphore.signal(). wait() and signal() will wait for each other for eternity -> a guaranteed classic deadlock!
Forget the semaphore, and refactor the photosAuthorizationStatus() method to return the result via a closure, as suggested by Sagar Chauhan.
How can I prevent a block of code to be repeatedly accessed from the same thread?
Suppose, I have the next code:
func sendAnalytics() {
// some synchronous work
asyncTask() { _ in
completion()
}
}
I want to prevent any thread from accessing "// some synchronous work", before completion was called.
objc_sync_enter(self)
objc_sync_exit(self)
seem to only prevent accessing this code from multiple threads and don't save me from accessing this code from the single thread. Is there a way to do this correctly, without using custom solutions?
My repeatedly accessing, I mean calling this sendAnalytics from one thread multiple times. Suppose, I have a for, like this:
for i in 0...10 {
sendAnalytics()
}
Every next call won't be waiting for completion inside sendAnalytics get called (obvious). Is there a way to make the next calls wait, before completion fires? Or the whole way of thinking is wrong and I have to solve this problem higher, at the for body?
You can use a DispatchSemaphore to ensure that one call completes before the next can start
let semaphore = DispatchSemaphore(value:1)
func sendAnalytics() {
self.semaphore.wait()
// some synchronous work
asyncTask() { _ in
completion()
self.semaphore.signal()
}
}
The second call to sendAnalytics will block until the first asyncTask is complete. You should be careful not to block the main queue as that will cause your app to become non-responsive. It is probably safer to dispatch the sendAnalytics call onto its own serial dispatch queue to eliminate this risk:
let semaphore = DispatchSemaphore(value:1)
let analyticsQueue = DispatchQueue(label:"analyticsQueue")
func sendAnalytics() {
analyticsQueue.async {
self.semaphore.wait()
// some synchronous work
asyncTask() { _ in
completion()
self.semaphore.signal()
}
}
}
I have this Swift code (migrated from Obj-C)
NSOperationQueue().addOperationWithBlock({
print("WORKING...")
NSOperationQueue.mainQueue().addOperationWithBlock({
print("FINISHED")
})
})
The mainQueue block doesn't execute ("FINISHED" is never printed). What's wrong with it?
This code is perfectly fine and both the operation block executing but the mainQueue operation block sometimes takes lil time to execute because mainQueue adds that block to the operation queue of the main thread but does not guarantee when it will be executed. There could be other items in that queue still waiting to execute.
I use GCD e.g:
dispatch_async(dispatch_get_main_queue()) {
[weak self] in
self.collection.reloadData()
}