1

I have a group of objects that I have to iterate through using a for-loop and DispatchGroup. When leaving the group inside the for-loop, is calling continue necessary?

let group = DispatchGroup()

for object in objects {

    group.enter()

    if object.property == nil {
         group.leave()
         continue // does calling this have any effect even though the group is handling it?
    }

    // do something with object and call group.leave() when finished
}
group.notify(...
Lance Samaria
  • 17,576
  • 18
  • 108
  • 256
  • By the way, you allude to “do something with object” ... Needless to say, if that “something” is not asynchronous, then you shouldn’t be using groups at all. – Rob Nov 01 '20 at 19:13
  • @Rob thanks, I wanted to keep the code as simple as possible just to get straight to the point. I wouldn’t use this for a regular loop. I actually use it to pull some data from Firebase where I have to go through multiple ref eg **postsRef > blockedRef > userIdRef > followingRef** – Lance Samaria Nov 01 '20 at 21:44
  • That makes sense. – Rob Nov 01 '20 at 21:49

2 Answers2

2

Yes, it is absolutely necessary to call continue, since you want to avoid continuing the execution of the body of your loop.

Calling DispatchGroup.leave does not exit the current scope, you need to call continue to achieve that. leave only affects whatever you are doing with the DispatchGroup - so consequent notify or wait calls.

Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116
1

Yes, the way this is written, the continue is critical, as you want to make sure that the enter call has a single leave call. Since you're calling enter before the if test, then you must leave and continue. If you don’t have the continue statement, it will proceed with the subsequent code which is calling leave already.

But this leave/continue pattern is not needed if you just call enter after the if statement:

let group = DispatchGroup()

for object in objects {    
    if object.property == nil {
         continue
    }

    group.enter()

    // do something with object and call group.leave() when finished
}
group.notify(queue: .main) { ... }

I'd then take that a step further and remove that if with the continue statement. Just add a where clause to the for loop, eliminating the need for the continue at all:

let group = DispatchGroup()

for object in objects where object.property != nil {
    group.enter()

    // do something with object and call group.leave() when finished
}

group.notify(queue: .main) { ... }

This accomplishes what your original code snippet did, but is more concise.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Hi, quick question. When using the `where` clause in a loop as in `for object in objects where object.postId == postId { }`, is there a need to add a `break` if the loop finds what it is looking for? Without `where` once it finds it, call `break` to exit the loop because there is no need to iterate any further. But I'm not clear if `where` automatically triggers `break`. – Lance Samaria Jan 23 '21 at 00:21
  • It depends upon your intent. If you really wanted to do it only for the first match, then the whole dispatch group pattern doesn’t make sense. The purpose for dispatch group is to know when all the asynchronous tasks are done. A “group” of one doesn’t make much sense. Lol. – Rob Jan 23 '21 at 00:54
  • Oh sorry, I should've been more clear, I meant without DispatchGroup, just a regular for-loop. And you're right, one group doesn't make sense, lol – Lance Samaria Jan 23 '21 at 01:41
  • If you only wanted the first one, you probably wouldn’t use a `for` loop, either, even with `where` clause. Just [`first(where:)`](https://developer.apple.com/documentation/swift/array/1848165-first) is probably appropriate if you just want the first entry that matches your `where` clause. – Rob Jan 23 '21 at 04:25
  • hmmmm, you're right about that, thanks! I have a bunch of for-loops where I search for one entry and I use break. I never thought about using `first(where)`. Thanks for the advice :) – Lance Samaria Jan 23 '21 at 04:28