Swift: إغلاقات Swift: تعابير الإغلاق والإغلاقات الزائدة

Closures are self-contained blocks of functionality in Swift that can capture and store references to variables and constants from their surrounding context. This lesson starts with closure expressions and builds toward mastering one of Swift's most flexible language features.

1. What You'll Learn


2. A Real-World Data Analyst Story

(1) Pain Point: Non-Reusable Sorting Logic and Verbose Code

Alice needs to sort user data — by name, by age, by registration date. Her initial approach is to write a separate function for each sort:

SWIFT
func sortByName(_ a: [String: Any], _ b: [String: Any]) -> Bool {
    return (a["name"] as! String) < (b["name"] as! String)
}
func sortByAge(_ a: [String: Any], _ b: [String: Any]) -> Bool {
    return (a["age"] as! Int) < (b["age"] as! Int)
}

Every time the sort rule changes, she has to write a new function, causing code bloat. Sorting logic is scattered across multiple functions — you can't specify it dynamically at the call site.

(2) The Closure Solution

Swift closures let you inline the sort rule directly at the sorted(by:) call site:

SWIFT
let sortedByAge = users.sorted { $0["age"] as! Int < $1["age"] as! Int }
let sortedByName = users.sorted { $0["name"] as! String < $1["name"] as! String }

The sorting logic shifts from "write a function → pass it" to "define it where you use it."

(3) Benefit: Inline Definition, Flexible Switching

Dimension Standalone Function Inline Closure
Lines of Code 3-5 lines per sort 1 line
Readability Need to find function definition Logic visible at call site
Flexibility All strategies must be pre-defined Defined on the fly
Context Access Cannot capture external variables Can capture contextual state

3. Closure Expressions

(1) Basic Syntax

A closure expression is a concise way to write a closure. The full syntax is:

100%
graph LR
    A[Closure Expression] --> B["{ (parameters) -> ReturnType in"]
    A --> C["    statements"]
    A --> D["}"]
    B --> E["{ (a: Int, b: Int) -> Bool in"]
    C --> F["    return a < b"]
    D --> G["}"]
Component Required Notes
{ } Yes Closures are wrapped in curly braces
Parameter list No Can be omitted using shorthand names
-> ReturnType No Can be inferred automatically
in Conditional Required when parameters or return type are declared

▶ Example: Three Ways to Write Closure Expressions

SWIFT
// ============================================
// Three ways to write closures for sorted(by:)
// ============================================
let numbers = [5, 2, 8, 1, 9]
// Approach 1: Full closure expression
let sorted1 = numbers.sorted(by: { (a: Int, b: Int) -> Bool in
    return a < b
})
// Approach 2: Type inference (omit parameter types and return type)
let sorted2 = numbers.sorted(by: { a, b in return a < b })
// Approach 3: Omit return (single-expression implicit return)
let sorted3 = numbers.sorted(by: { a, b in a < b })
print("sorted3: \(sorted3)")

Output:

TEXT 📖 للعرض فقط
sorted3: [1, 2, 5, 8, 9]

4. Trailing Closures and Shorthand Parameter Names

(1) Trailing Closures

When a closure is the last argument of a function, you can move it outside the parentheses:

Style Example
Standard sorted(by: { a, b in a < b })
Trailing sorted { a, b in a < b }
Multi-parameter trailing animate(duration: 1) { ... }

▶ Example: Simplifying Calls with Trailing Closures

SWIFT
// ============================================
// Trailing closure concise syntax
// ============================================
let scores = [78, 92, 85, 68, 99]
// Standard syntax
let ascending1 = scores.sorted(by: { a, b in a < b })
// Trailing closure syntax
let ascending2 = scores.sorted { a, b in a < b }
print("Ascending: \(ascending2)")
// Multiple closure parameters also supported
func performRequest(url: String, onSuccess: (String) -> Void, onError: (Error) -> Void) {
    // Simulate network request
    onSuccess("Data received")
}
performRequest(url: "https://api.example.com") { data in
    print("Success: \(data)")
} onError: { error in
    print("Error: \(error)")
}

Output:

TEXT 📖 للعرض فقط
Ascending: [68, 78, 85, 92, 99]
Success: Data received

(2) Shorthand Parameter Names

Swift automatically provides shorthand names for inline closure parameters: $0, $1, $2, ...

Full Syntax Shorthand
{ a, b in a < b } { $0 < $1 }
{ name in print(name) } { print($0) }
{ a, b, c in a + b + c } { $0 + $1 + $2 }

▶ Example: Shorthand Parameter Names in Action

SWIFT
// ============================================
// Step-by-step simplification with shorthand names
// ============================================
let words = ["banana", "apple", "cherry", "date"]
// Full syntax
let sortedA = words.sorted(by: { (a: String, b: String) -> Bool in
    return a < b
})
// Shorthand parameter names — most concise
let sortedB = words.sorted(by: { $0 < $1 })
// Even simpler — pass the operator function directly
let sortedC = words.sorted(by: <)
print(sortedC)

Output:

TEXT 📖 للعرض فقط
["apple", "banana", "cherry", "date"]

Common Pitfall: When using shorthand parameter names, you can omit return if the closure body contains only a single expression. If there are multiple statements, you must write return.


5. Value Capture

A closure can capture constants and variables from its surrounding context, even after that context no longer exists.

100%
graph TB
    A[Outer function makeIncrementer] --> B["Declare variable total = 0"]
    A --> C["Return closure { total += amount; return total }"]
    C --> D[Closure captures reference to total]
    D --> E[Even after makeIncrementer returns]
    E --> F["total remains alive inside the closure"]

▶ Example: Closure Capturing Context Variables

SWIFT
// ============================================
// Value capture — generating an accumulator
// ============================================
func makeIncrementer(step: Int) -> () -> Int {
    var total = 0
    let incrementer: () -> Int = {
        total += step  // Captures total and step
        return total
    }
    return incrementer
}
let incrementByTwo = makeIncrementer(step: 2)
print(incrementByTwo()) // 2
print(incrementByTwo()) // 4
print(incrementByTwo()) // 6

Output:

TEXT 📖 للعرض فقط
2
4
6

Tip: Value capture is one of the most powerful features of closures. Swift automatically manages the memory of captured variables — a closure holds a strong reference to captured variables until the closure itself is released.

▶ Example: Custom Sorting Rules

SWIFT
// ============================================
// Custom sorting rules with closures
// ============================================
struct Product {
    let name: String
    let price: Double
    let rating: Int
}
let products = [
    Product(name: "Laptop", price: 1299, rating: 5),
    Product(name: "Mouse", price: 29, rating: 4),
    Product(name: "Keyboard", price: 99, rating: 5),
    Product(name: "Monitor", price: 399, rating: 3)
]
// Sort by price ascending
let byPrice = products.sorted { $0.price < $1.price }
print("Cheapest: \(byPrice.first!.name)")
// Sort by rating descending, then by price ascending on tie
let byRatingThenPrice = products.sorted {
    if $0.rating != $1.rating {
        return $0.rating > $1.rating
    }
    return $0.price < $1.price
}
print("Best value: \(byRatingThenPrice.first!.name)")

Output:

TEXT 📖 للعرض فقط
Cheapest: Mouse
Best value: Keyboard

6. Complete Example: User List Sorter

SWIFT
// ============================================
// Complete example: User list sorter
// Features: Closure expressions + trailing closures + shorthand names + value capture
// ============================================
import Foundation
// 1. User model
struct User {
    let name: String
    let age: Int
    let score: Double
}
let users = [
    User(name: "Alice", age: 28, score: 92.5),
    User(name: "Bob", age: 22, score: 85.0),
    User(name: "Charlie", age: 35, score: 95.5),
    User(name: "Diana", age: 19, score: 78.0)
]
// 2. Sorter factory — captures sort direction
func makeSorter(ascending: Bool) -> (User, User) -> Bool {
    return { ascending ? $0.score < $1.score : $0.score > $1.score }
}
// 3. Sort using shorthand parameter names
let ascendingSorter = makeSorter(ascending: true)
let sortedAsc = users.sorted(by: ascendingSorter)
print("Ascending:")
for u in sortedAsc {
    print("  \(u.name): \(u.score)")
}
// 4. Inline sort with trailing closure
let sortedDesc = users.sorted { $0.score > $1.score }
print("Descending:")
for u in sortedDesc {
    print("  \(u.name): \(u.score)")
}

Output:

TEXT 📖 للعرض فقط
Ascending:
  Diana: 78.0
  Bob: 85.0
  Alice: 92.5
  Charlie: 95.5
Descending:
  Charlie: 95.5
  Alice: 92.5
  Bob: 85.0
  Diana: 78.0

❓ FAQ

س What's the difference between a closure and a function?
ج Closures are anonymous functions with a more concise syntax. Functions have names and can be called repeatedly; closures are typically used for one-off scenarios. Closures can capture contextual variables — nested functions can too, but regular global functions cannot.
س When must I write the in keyword?
ج When a closure expression declares a parameter list or return type, you must use in to separate the declaration from the body. If you use shorthand parameter names (like $0) and omit all type annotations, in is not needed.
س How is the order of $0 and $1 determined?
ج $0 is the first parameter of the closure, $1 is the second, and so on. The order is determined by the order in which arguments are passed when the closure is called, not by parameter names.
س Are captured values copies or references?
ج Closures capture variables by reference (similar to inout) — when multiple closures share the same variable, changes in one affect the other. Constants are captured by value copy.
س What if a closure is too long and hurts readability?
ج If the closure body exceeds 5-10 lines, extract it into a standalone function or a named closure variable before passing it in. For example: let sorter = { a, b in ... }; sorted(by: sorter).

📖 Summary


📝 Exercises

  1. Basic: Given the integer array [3, 7, 1, 9, 4, 6], write sorted(by:) three different ways (full closure, type inference, shorthand parameter names) to sort in descending order.
  2. Intermediate: Write a function repeatTask(times:task:) that takes a count and a closure, executing the closure the specified number of times. Then call it to print "Hello, Swift!" 5 times.
  3. Challenge: Implement a makeCalculator function that takes an operator string (+, -, *, /) and returns the corresponding (Int, Int) -> Int closure. Then use the function to create an addition closure and a multiplication closure, calculating each for (10, 5).
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%