What happens if my DispatchGroup never finishes - ios

I have many asynchronous tasks that need to all be completed before I can move on, so I'm using a DispatchGroup. However, I am able to tell if one of the tasks fails early, and I would usually call a completion handler.
If I leave the scope of the function by calling a completion handler, what happens to the DispatchGroup?
Is the memory allocated forever? Is it possible that if leave is called enough times eventually (maybe due to a bug) that the notify block can still be called?
Example:
func example(completion: #escaping (Bool) - Void) {
let group = DispatchGroup()
group.enter()
asyncFunction1 {
if result == false {
completion(false)
} else {
group.leave()
}
}
group.enter()
asyncFunction2 { result in
if result == true {
group.leave()
}
}
group.notify(queue: .main) {
completion(true)
}
}
In the example above I have two asynchronous functions. The first function might call completion(false), and the second function only calls leave on success. While it might not be the best example of code, these conditions are possible. What happens to the notify block?

From the swift's source on GCD:
https://github.com/apple/swift-corelibs-libdispatch/blob/master/src/semaphore.c
It seems the block will be allocated and never released. Meaning, the memory the block retains will also never be released.

The dispatchgroup must be notified whenever a code block enters the group as well as whenever a codeblock leaves the group.
That means, dispatchGroup.leave() must be called regardless of success or failure of your function.
When all blocks are finished, the dispatchgroup gets notified.

I'm wondering what happens when leave is never called. Just because it must be called, doesn't mean it always will be called.
Well, don't "never call" it. You must arrange your code so that leave is called by every possible exit path. If you can't, don't use dispatch groups.
If necessary, you can pass the dispatch group itself into the completion handler so that it can call leave for you. But one way or another, call leave.

Related

Understanding escaping closures Swift

Need help with escaping closures.
I understand that the definition of escaping closures is If a closure is passed as an argument to a function and it is invoked after the function returns, the closure is escaping.
I believe there are a few scenarios where escaping closures are necessary.
Setting an outside variable as the passing closure
I understand this because the variable lives even after the function returns.
Asynchronous operations
Things like Dispatch.main.async{} or Network Request.This is the part the I don't quite understand. I understand that Dispatch.main.async{} runs on a different thread and that async operations take a while to complete hence the name async. However, I cannot understand how these operations outlive the scope of the function.
When you call DispatchQueue.main.async{} you are creating a closure (a block in Objective-C) that is sent to the given dispatch queue. Your function will then continue running after the async call and could end before the block has a chance to run on the DispatchQueue. The closure has "escaped"!
The code in the block will actually be run when the block reaches the front of its DispatchQueue
The async doesn't mean that the operation will take a long time, it means that this function (the one calling async) will not pause to wait on the block to complete.
(if you DID want to pause and wait for the operation to complete, you could call sync instead of async).
When you do asynchronous work it might escape the current scope cause, by definition, it won't be waited for its completion.
This is also why the terminology completion is used for those closures adopted in asynchronous code for the delivery of results.
Here's an example, assuming queue is a DispatchQueue and someHugeWork() -> R is a synchronous function which returns a result R:
func doItSync() -> R {
var result: R!
queue.sync {
result = someHugeWork()
}
// the function has to wait for the code submitted synchronously
// on the queue.
return result
}
func doItAsync(completion: #escaping (R) -> Void) {
queue.async {
let result = someHugeWork()
completion(result)
}
// work was submitted asynchronously on the queue, hence the function
// just returns, therefore the completion closure might escape this
// scope.
}
Now, obviously in this case doItSync() does not really make sense, since it'll block the current thread until someHugeWork() has executed on a different queue. That is also why you can return the result from its scope: it waits for it. I've only added this to let you see the difference between synchronous code and asynchronous code for returning a result.
On the other hand doItAsync(completion:) just returns immediately without having to wait for someHugeWork() to complete on a different queue, hence it won't block the current thread.
Since you've submitted this work asynchronously on the queue you now have no control about when it will be executed and that is also why your only way to return the result from someHugeWork() is by using a completion closure, which has to be captured inside the work item submitted asynchronously… If you think about it for a moment it'll be as if such completion closure is stored to be retrieved by the queue at a later time when it will executes the work item submitted asynchronously (you said in your question that you did got the concept of storing a closure).
Now let's also see a method which doesn't need an escaping closure, that'll be the body parameter of Sequence's forEach(_:) method: this method executes the given closure on every element of the sequence, that'll be as in doing:
func forEach(_ body: (Element) -> Void) {
for element in self {
body(element)
}
// the function returns only after the for-in loop has completed,
// hence body closure only executes in the scope of this method,
// thus it never escapes.
}
(for the sake of simplicity I've omitted the throwing annotations)
In this case the body closure never escapes forEach(_:) method's scope, therefore it doesn't need to be annotated as #escaping: it will only executes inside the for-in loop which has to complete before the method can return.

When would a queue consider a task is completed?

In the following code, when would queueT (serial queue) consider “task A” is completed?
The moment when aNetworkRequest switched to another thread?
Or in the doneInAnotherQueue block? ( commented // 1)
In another word, when would “task B” be executed?
let queueT = DispatchQueue(label: "com.test.a")
queueT.async { // task A
aNetworkRequest.doneInAnotherQueue() { // completed in another thread possibly
// 1
}
}
queueT.async { // task B
print("It's my turn")
}
It would much better if you could explain the mechanism how a queue consider a task is completed.
Thanks in advance.
In short, the first example starts an asynchronous network request, so the async call “finishes” as soon as that network request is submitted (but does not wait for that network request to finish).
I am assuming that the real question is that you want to know when the network request is done. Bottom line, GCD is not well suited for managing dependencies between tasks that are, themselves, asynchronous requests. The dispatching the initiation of a network request to a serial queue is undoubtedly not going to achieve what you want. (And before someone suggests using semaphores or dispatch groups to wait for the asynchronous request to finish, note that can solve the tactical issue, but it is a pattern to be avoided because it is inefficient use of resources and, in edge cases, can introduce deadlocks.)
One pattern is to use completion handlers:
func performRequestA(completion: #escaping () -> Void) { // task A
aNetworkRequest.doneInAnotherQueue() { object in
...
completion()
}
}
Now, in practice, we would generally use the completion handler with a parameter, perhaps even a Result type:
func performRequestA(completion: #escaping (Result<Foo, Error>) -> Void) { // task A
aNetworkRequest.doneInAnotherQueue() { result in
guard ... else {
completion(.failure(error))
return
}
let foo = ...
completion(.success(foo))
}
}
Then you can use the completion handler pattern, to process the results, update models, and perhaps initiate subsequent requests that are dependent upon the results of this request. For example:
performRequestA { result in
switch result {
case .failure(let error):
print(error)
case .success(let foo):
// update models or initiate next step in the process here
}
}
If you are really asking how to manage dependencies between asynchronous tasks, there are a number of other, elegant patterns (e.g., Combine, custom asynchronous Operation subclass, the forthcoming async/await pattern contemplated in SE-0296 and SE-0303, etc.). All of these are elegant solutions for managing dependencies between asynchronous tasks, controlling the degree of concurrency, etc.
We probably would need to better understand the nature of your broader needs before we made any specific recommendations. You have asked the question about a single dispatch, but the question probably is best viewed from a broader context of what you are trying to achieve. For example, I'm assuming you are asking because you have multiple asynchronous requests to initiate: Do you really need to make sure that they happen sequentially and lose all the performance benefits of concurrency? Or can you allow them to run concurrently and you just need to know when all of the concurrent requests are done and how to get the results in the correct order? And might you have so many concurrent requests that you might need to constrain the degree of concurrency?
The answers to those questions will probably influence our recommendation of how to best manage your multiple asynchronous requests. But the answer is almost certainly is not a GCD queue.
You can do a simple check
let queueT = DispatchQueue(label: "com.test.a")
queueT.async { // task A
DispatchQueue(label: "com.test2.a").async { // create another queue inside
for i in 0..<6 {
print(i)
}
}
}
queueT.async { // task B
for i in 10..<20 {
print(i)
}
}
}
you'll get different output each run this means yes when you switch thread the task is considered done
A GCD work item is complete when the closure you pass returns. So for your example, I'm going to rewrite it to make the function calls and parameters more explicit (rather than using trailing closure syntax).
queueT.async(execute: {
// This is a function call that takes a closure parameter. Whether this
// function returns, then this closure will continue. Whether that is before or
// after running completionHandler is an internal detail of doneInAnotherQueue.
aNetworkRequest.doneInAnotherQueue(closureParameter: { ... })
// At this point, the closure is complete. What doneInAnotherQueue() does with
// its closure is its business.
})
Assuming that doneInAnotherQueue() executes its closure parameter "sometime in the future", then your task B will likely run before that closure runs (it may not; it's really a race at that point, but probably). If the doneInAnotherQueue() blocks on its closure before returning, then closureParameter will definitely run before task B.
There is absolutely no magic here. The system has no idea what doneInAnotherQueue does with its parameter. It may never run it. It may run it immediately. It may run it sometime in the future. The system just calls doneInAnotherQueue() and passes it a closure.
I rewrote async in normal "function with parameters" syntax to make it even more clear that async() is just a function, and it takes a closure parameter. It also isn't magic. It's not part of the language. It's just a normal function in the Dispatch framework. All it does it take its parameter, put it on a dispatch queue, and return. It doesn't execute anything. There's just closures that get put on queues, scheduled, and executed.
Swift is in the process of adding structured concurrency, which will add more language-level concurrency features that will allow you to express much more advanced things than the simple primitives provided by GCD.
Your task A returns straight away. Dispatching work to another queue is synchronous. Think of the block (the trailing closure) after 'doneInAnotherQueue' as just an argument to the doneInAnotherQueue function, no different to passing an Int or a String. You pass that block along and then you return immediately with the closing brace from task A.

Variable becomes nil outside completion handler [duplicate]

This question already has an answer here:
Swift || Returning a class that can be used by other methods from an API call
(1 answer)
Closed 3 years ago.
I am not sure if this is a duplicate or not, I searched and couldn't find an answer. If it is please point me to the similar question.
I am setting the value of the imageURL variable inside a completion handler:
var imageURL: URL?
let options = PHContentEditingInputRequestOptions()
imageAsset.requestContentEditingInput(with: options, completionHandler: { (contentEditingInput: PHContentEditingInput?, _) in
imageURL = contentEditingInput?.fullSizeImageURL
})
//prints nil
print(imageURL)
Now, the value of imageURL inside the handler is not nil, but it becomes nil right after (in the print statement). Why is this happening and how can I fix it without having to write all of my subsequent code inside the completion handler block?
You can't "fix" it in the way you'd like, unfortunately. The completion handler here might in theory be called synchronously (ie, at the time of the call to requestContentEditingInput), but can (and most likely will) be called at some time later, when the asset is ready. That could include actual downloading or any other unpredictable time-consuming preparation of the asset that happens on some other thread.
In other words, the function requestContentEditingInput returns to you right away (and your code continues executing), but the function also commences doing some work in the background. That background work, when it finishes, calls your handler block.
The nature of execution flow means that you simply cannot guarantee (and certainly cannot assume) that the handler will be called before execution moves on to your print(imageURL) line.
This kind of asynchronicity is a very common pattern, though! Nothing to be afraid of. You need to put any code that must run subsequently within that handler block (or call out from the handler block to another function if that is cleaner within your file).
It is likely that the completion is not called until later because the code is running on another thread. Try putting a print statement inside of the completion block to see what order the code is executed.
When you work with handlers, the time with the threads can be different every build.
I recommend you create a method that will be called inside the handler.
Like this:
func edit(){
var imageURL: URL?
let options = PHContentEditingInputRequestOptions()
imageAsset.requestContentEditingInput(with: options, completionHandler: { (contentEditingInput: PHContentEditingInput?, _) in
imageURL = contentEditingInput?.fullSizeImageURL
display(url:imageURL)
})
}
func display(url: String) {
print(imageURL)
}

Does return need to be called after an escaping completion block?

If I have the following code:
func getData(completion: #escaping () -> ()) {
// Do stuff
completion()
print(hello)
}
When the completion block is called, will the function automatically return, or will it continue running and "hello" be printed?
Completion handlers are just like any other ordinary closure. In some cases people might want to run some more code after completion, so that's why they designed them like so. When you call them, they run. And after they return, the code below runs. They cannot replace a return statement. If you want to stop running the rest of the method after the completion() bit, put a return there.
Unless, your completion handler is declared to return Never, in which case it will either terminate your whole app, or run indefinitely. In that case any code after it won't be run. :)

Swift function does not return soon enough

I'm trying to move my app over to MVC, I have a Parse query which I've moved over to a function in my model class, the function returns a Bool.
When the button in my ViewController below is pressed the model function 'parseQuery' should be run, return a bool and then I need to use that bool to continue. At the moment, the if statement is executed before the function has completed so it always detects false.
How can I ensure that the if statement is completed once the function has completed?
#IBAction func showAllExpiredUsers(sender: AnyObject) {
var success = searchResults.parseQuery()
if success {
print("true")
} else {
print("false")
}
//I have also tried:
searchResults.parseQuery()
if searchResults.parseQuery() {
print("true")
} else {
print("false")
}
You have a few options, but the issue is due to asynchronous calls.
Does Parse expose the same function, with a completion block?
If yes, then you place the processing of the Bool inside the completion block, which is called when the async task is completed.
If not, which I doubt, you could create an NSOperationQueue, with a maxConcurrency of 1 (so it is serial) and dispatch the calls onto the queue with
func addOperationWithBlock(_ block: () -> Void)
This is called on the queue. You would need to store the success bool globally so that you can access it inside the second queued block operation, to check the success state.
Update:
I haven't used parse, but checking the documentation for findObjectsInBackgroundWithBlock (https://parse.com/docs/ios/guide#queries) it takes a completion block where you can process the result, update your bool.
I'm not sure what you are trying to do. You don't need to have the success state of the query. You can check
if (!error) {
// do stuff
} else {
//error occurred - print("error \(error.localizedDescription)"
}
Check the example.
What you need to understand is threading. The async task provides a completion block because its asynchronous, it gets dispatched onto another thread for processing. I'm not sure how much you know about threading but there is something called a thread pool. This thread pool is accessed by Queues. The thread pool is managed by the OS, and makes sure available threads can be used by queues that need work done. As users interact with an application, this (and all UI work) is done on the main thread.
So whenever some processing is going to interfere with possible interaction or UI updates, it should be dispatched (Grand Central Dispatch) or queued (NSOperationQueue, built on top of GCD) off of the main thread.
Anyway, this is why the findObjectsInBackgroundWithBlock call is dispatched off the main thread, because otherwise it would block the main thread until its done, ruining the experience for the user. Also, if the main thread is blocked for more than 1 minute (last time I checked), the OS's watchdog will kill your process.
So yeah, assigning a boolean to the return of the block, would get the return of the function, which occurs before the completion block is done. The completion block is where you code some stuff to be done after the function completes. So the query gets dispatched onto another thread and starts processing, the thread that sent this work off for processing, continues with the rest of its execution. So checking the boolean directly after, wouldn't work because the other thread isn't complete. Even if the other thread finished in time, what is connecting the background thread with the main thread?
This is the beauty of blocks (function pointers), it's a lot cleaner and optimized and keeps code compact. The old way, which is still is use for some older frameworks, is delegates, which detaches the calling code with the callback and adds a delegate dependency. Blocks are beautiful.
Its also important to note that completion blocks don't always get called on the main thread. In many cases its up to you to dispatch the work back to the main thread, to handle any UI work that needs to be done with the objects available inside the completion block.
The query likely takes some time to run and should be run in a background thread with a callback function to handle the response WHEN it completes.
look at the Documentation
Specifically looking at the query.findObjectsInBackgroundWithBlock code:
var query = PFQuery(className:"GameScore")
query.whereKey("playerName", equalTo:"Sean Plott")
query.findObjectsInBackgroundWithBlock {
(objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
// The find succeeded.
print("Successfully retrieved \(objects!.count) scores.")
// Do something with the found objects
if let objects = objects as? [PFObject] {
for object in objects {
print(object.objectId)
}
}
} else {
// Log details of the failure
print("Error: \(error!) \(error!.userInfo!)")
}
}
The above code will execute the query and run the code in the block when it gets the results from Parse. This is known as an asynchronous task, for more information check out this guide

Resources