Swift exit for loop from within an autoreleasepool?

1.3k Views Asked by At

Assuming I have the following in a loop:

for i in 0...10 {
    autoreleasepool {
        // do some crazy memory thing
    }
}

How would I go about breaking out of the for loop from within the autoreleasepool block?

3

There are 3 best solutions below

0
Rob On BEST ANSWER

I infer that the intent of this question was to be able to post an answer, showing how autoreleasepool is actually a generic method, returning whatever value was returned by its closure. It was suggested that one could do:

for i in 0...10 {
    if (
        !autoreleasepool {
            // do stuff
            // return false for break, true for continue.
            return true
        }
    ) {
        break
    }
}

While it’s revelatory when we first discover that many of these synchronous closure methods actually are generics that return whatever the closure does, I don’t think this is a particularly good example of its application. If you need comments in your code to explain what the return value means, that’s code smell for a deeper problem. I think rmaddy’s approach (+1) is much more clear, much easier to reason about. There are good uses of autoreleasepool returning the same Result as the closure, but this isn’t it, IMHO.


Let’s consider a more compelling use of autoreleasepool return types. Let’s imagine that you have some routine that does the following (I’ve removed the GCD calls to keep our attention on the autoreleasepool):

func generateAllImages() {
    for index in 0 ..< imageCount {
        let image = generateImage(for: index)
        updateUserInterface(for: index, with: image)
    }
}

And let’s say that during the profiling of the app, that we discover that buried inside generateImage was something that created an autorelease object, which made the peak memory usage of our app really spike. Clearly, you could do the following to reduce the high water mark of the app, draining the autorelease pool at each iteration:

func generateAllImages() {
    for index in 0 ..< imageCount {
        autoreleasepool {
            let image = generateImage(for: index)
            updateUserInterface(for: index, with: image)
        }
    }
}

But if you’ve confirmed that your autorelease object was limited inside the scope of the generateImage routine, you could can tidy this up a bit:

func generateAllImages() {
    for index in 0 ..< imageCount {
        let image = autoreleasepool { generateImage(for: index) }
        updateUserInterface(for: index, with: image)
    }
}

That’s not only more concise, but makes it clear where the autorelease object was created. This pattern strikes me as a very natural and compelling use of the autoreleasepool behavior of returning the object that its closure returns.

0
Nathan F. On

The solution I came to for this was to return from the autoreleasepool block a boolean, true for continue or false for break. the autoreleasepool block is apparently blocking.

for i in 0...10 {
    if (
        !autoreleasepool {
            // do stuff
            // return false for break, true for continue.
            return true
        }
    ) {
        break
    }
}
0
rmaddy On

Setup a variable you can optionally set inside the autorelease pool:

for i in 0...10 {
    var stop = false
    autoreleasepool {
        // lots of stuff

        stop = true // call this if you need to stop
    }
    if stop { break }
}