Lesson 46Closures
Autoclosures
What is @autoclosure?
The @autoclosure attribute automatically wraps an expression in a closure. This delays evaluation and makes the syntax cleaner!
Comparison
Without @autoclosure
check({ 5 > 3 })Must use braces
With @autoclosure
check(5 > 3)Clean expression syntax
Key Benefits
- Cleaner syntax - No braces needed
- Delayed evaluation - Computed only when called
- Short-circuit - Skip expensive operations
When to Use
- Assert/precondition functions
- Default value providers
- Conditional logging
- Short-circuit evaluation
Combining with @escaping
func store(_ check: @autoclosure @escaping () -> Bool)
Use both when you need to store the autoclosure for later execution.
main.swift
// AUTOCLOSURES
// Automatically wraps an expression in a closure
// Delays evaluation until called
// WITHOUT @autoclosure
func logIfTrue(_ condition: () -> Bool, _ message: String) {
if condition() {
print(message)
}
}
// Must pass a closure with braces
logIfTrue({ 5 > 3 }, "Five is greater!")
// WITH @autoclosure
func logIfTrueAuto(_ condition: @autoclosure () -> Bool, _ message: String) {
if condition() {
print(message)
}
}
// Can pass expression directly - cleaner!
logIfTrueAuto(5 > 3, "Five is greater!")
// DELAYED EVALUATION
var counter = 0
func incrementAndCheck() -> Bool {
counter += 1
print("Counter is now: \(counter)")
return counter > 5
}
// Without autoclosure - called immediately
func regularCheck(_ check: Bool) {
print("Regular: \(check)")
}
// With autoclosure - called only when needed
func autoCheck(_ check: @autoclosure () -> Bool) {
print("About to check...")
print("Auto: \(check())") // NOW it's called
}
// PRACTICAL EXAMPLE: Assert-like function
func myAssert(_ condition: @autoclosure () -> Bool,
_ message: @autoclosure () -> String) {
#if DEBUG
if !condition() {
print("Assertion failed: \(message())")
}
#endif
}
myAssert(1 + 1 == 2, "Math is broken!")
// COMBINING @autoclosure AND @escaping
var pendingChecks: [() -> Bool] = []
func addPendingCheck(_ check: @autoclosure @escaping () -> Bool) {
pendingChecks.append(check)
}
addPendingCheck(counter > 10)
addPendingCheck(counter < 100)
// Run checks later
for check in pendingChecks {
print("Check result: \(check())")
}
// SHORT-CIRCUIT EVALUATION (like || and &&)
func orElse(_ a: Bool, _ b: @autoclosure () -> Bool) -> Bool {
if a { return true }
return b() // Only evaluated if a is false
}
let result = orElse(true, expensiveOperation())
// expensiveOperation() is NEVER called!
// REAL-WORLD: Nil-coalescing alternative
func unwrap<T>(_ optional: T?, default defaultValue: @autoclosure () -> T) -> T {
if let value = optional {
return value
}
return defaultValue() // Only computed if needed
}
let name: String? = nil
let greeting = unwrap(name, default: "Guest")
print(greeting) // GuestTry It Yourself!
Create a function debug(_ message: @autoclosure () -> String) that only prints in debug mode!