Search code examples
swiftfor-loopbreaknsautoreleasepool

Swift exit for loop from within an autoreleasepool?


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?


Solution

  • 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.