728x90
Closure
명명된 함수 생성없이 실행되는 코드의 그룹을 말한다
이 클로저는 정의된 context 에서 상수, 변수에 대한 참조를 캡처하고 저장할 수 있다.
이러한 상수와 변수를 closing over 라고 한다.
Swift 는 캡처의 모든 메모리 관리를 처리한다.
클로저는 세가지의 형태 중 하나의 모습을 한다
- 전역 함수 : 이름을 가지고 어떠한 값도 캡처하지 않음
- 중첩 함수 : 둘러싼 함수로 부터 값을 캡처
- 주변 context 의 값을 캡처할 수 있는 경량 구문으로 작성되는 이름 없는 클로저
Function 과 Closure
import SwiftUI
struct ClosureFunctionDemoView: View {
// MARK: - 1. Function (이름 있는 함수)
func greetFunction(name: String) -> String {
return "Hello from function, \(name)"
}
// MARK: - 2. Closure (변수에 저장된 익명 함수)
let greetClosure: (String) -> String = { (name: String) -> String in
return "Hello from closure, \(name)"
}
// MARK: - 3. Closure 축약형들
let square1: (Int) -> Int = { (num: Int) -> Int in return num * num }
let square2: (Int) -> Int = { (num: Int) in return num * num }
let square3: (Int) -> Int = { (num: Int) in num * num }
let square4: (Int) -> Int = { $0 * $0 }
// MARK: - 4. map 고차함수와 익명 클로저
let names = ["Lee", "Kim", "Park"]
// MARK: - 5. 변수 캡처 예시
func makeIncrementer(by amount: Int) -> () -> Int {
var total = 0
return {
total += amount
return total
}
}
@State private var incrementResult: [Int] = []
@State private var selectedExample = 0
@State private var showExplanation = false
// MARK: - 각 예제에 대한 설명
let explanations = [
"함수는 코드 블록에 이름을 붙인 것입니다. 매개변수와 반환 타입을 명시하고, 함수 이름으로 호출합니다.",
"클로저는 이름 없는 함수로, 변수에 저장하거나 다른 함수에 인자로 전달할 수 있습니다. 기본 문법은 { (매개변수) -> 반환타입 in 실행코드 } 형태입니다.",
"클로저는 여러 가지 방법으로 축약할 수 있습니다:\n1. 타입 추론으로 매개변수와 반환 타입 생략\n2. return 키워드 생략 (단일 표현식일 때)\n3. 매개변수 이름 대신 $0, $1 등의 위치 기반 이름 사용",
"map, filter, reduce와 같은 고차함수는 클로저를 인자로 받아 컬렉션의 각 요소에 적용합니다. 이를 통해 간결하고 함수형 프로그래밍 스타일의 코드를 작성할 수 있습니다.",
"클로저는 생성된 컨텍스트의 변수를 '캡처'할 수 있습니다. 클로저가 생성된 이후에도 이 변수에 접근하고 수정할 수 있는 것이 클로저의 강력한 특성입니다."
]
// MARK: - View Body
var body: some View {
NavigationView {
VStack {
// 선택 세그먼트
Picker("예제 선택", selection: $selectedExample) {
Text("함수").tag(0)
Text("클로저").tag(1)
Text("축약형").tag(2)
Text("고차함수").tag(3)
Text("변수캡처").tag(4)
}
.pickerStyle(SegmentedPickerStyle())
.padding()
ScrollView {
VStack(alignment: .leading, spacing: 20) {
// 예제별 내용 표시
Group {
if selectedExample == 0 {
exampleOne
} else if selectedExample == 1 {
exampleTwo
} else if selectedExample == 2 {
exampleThree
} else if selectedExample == 3 {
exampleFour
} else if selectedExample == 4 {
exampleFive
}
}
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(8)
// 설명 섹션
if showExplanation {
Text("📝 설명")
.font(.headline)
Text(explanations[selectedExample])
.padding()
.background(Color.blue.opacity(0.1))
.cornerRadius(8)
}
Spacer()
}
.padding()
}
Button(showExplanation ? "설명 숨기기" : "설명 보기") {
withAnimation {
showExplanation.toggle()
}
}
.padding()
.buttonStyle(.bordered)
}
.navigationTitle("Swift 클로저 강의")
.navigationBarTitleDisplayMode(.inline)
}
}
// MARK: - 예제별 View
var exampleOne: some View {
VStack(alignment: .leading, spacing: 12) {
Text("1️⃣ 일반 함수 (Function)")
.font(.headline)
Text("함수 선언:")
.fontWeight(.medium)
Text("""
func greetFunction(name: String) -> String {
return "Hello from function, \\(name)"
}
""")
.font(.system(.body, design: .monospaced))
.padding(8)
.background(Color.black.opacity(0.03))
.cornerRadius(4)
Text("함수 호출 결과:")
.fontWeight(.medium)
Text(greetFunction(name: "Taenee"))
.foregroundColor(.blue)
}
}
var exampleTwo: some View {
VStack(alignment: .leading, spacing: 12) {
Text("2️⃣ 클로저 (Closure)")
.font(.headline)
Text("클로저 선언:")
.fontWeight(.medium)
Text("""
let greetClosure: (String) -> String = { (name: String) -> String in
return "Hello from closure, \\(name)"
}
""")
.font(.system(.body, design: .monospaced))
.padding(8)
.background(Color.black.opacity(0.03))
.cornerRadius(4)
Text("클로저 호출 결과:")
.fontWeight(.medium)
Text(greetClosure("Taenee"))
.foregroundColor(.blue)
}
}
var exampleThree: some View {
VStack(alignment: .leading, spacing: 12) {
Text("3️⃣ 클로저 축약형")
.font(.headline)
Text("4가지 방식의 클로저:")
.fontWeight(.medium)
Text("""
// 1. 표준 형태
let square1: (Int) -> Int = { (num: Int) -> Int in return num * num }
// 2. 반환 타입 추론
let square2: (Int) -> Int = { (num: Int) in return num * num }
// 3. return 키워드 생략
let square3: (Int) -> Int = { (num: Int) in num * num }
// 4. 단축 인자 이름 ($0)
let square4: (Int) -> Int = { $0 * $0 }
""")
.font(.system(.body, design: .monospaced))
.padding(8)
.background(Color.black.opacity(0.03))
.cornerRadius(4)
Text("모든 클로저 호출 결과 (3의 제곱):")
.fontWeight(.medium)
Text("square1(3) = \(square1(3))")
.foregroundColor(.blue)
Text("square2(3) = \(square2(3))")
.foregroundColor(.blue)
Text("square3(3) = \(square3(3))")
.foregroundColor(.blue)
Text("square4(3) = \(square4(3))")
.foregroundColor(.blue)
}
}
var exampleFour: some View {
VStack(alignment: .leading, spacing: 12) {
Text("4️⃣ 고차함수와 클로저")
.font(.headline)
Text("배열과 map 사용:")
.fontWeight(.medium)
Text("""
let names = ["Lee", "Kim", "Park"]
// map으로 모든 요소 대문자화
names.map { $0.uppercased() }.joined(separator: ", ")
""")
.font(.system(.body, design: .monospaced))
.padding(8)
.background(Color.black.opacity(0.03))
.cornerRadius(4)
Text("map 결과:")
.fontWeight(.medium)
Text(names.map { $0.uppercased() }.joined(separator: ", "))
.foregroundColor(.blue)
// 다른 고차함수 예제
Divider()
Text("다른 고차함수 예제:")
.fontWeight(.medium)
Text("• filter: \(names.filter { $0.count > 3 }.joined(separator: ", "))")
Text("• contains: \(names.contains { $0.starts(with: "P") } ? "P로 시작하는 이름 있음" : "P로 시작하는 이름 없음")")
Text("• sorted: \(names.sorted().joined(separator: ", "))")
}
}
var exampleFive: some View {
VStack(alignment: .leading, spacing: 12) {
Text("5️⃣ 클로저의 변수 캡처")
.font(.headline)
Text("변수 캡처 코드:")
.fontWeight(.medium)
Text("""
func makeIncrementer(by amount: Int) -> () -> Int {
var total = 0
return {
total += amount // 외부 변수 total을 캡처
return total
}
}
""")
.font(.system(.body, design: .monospaced))
.padding(8)
.background(Color.black.opacity(0.03))
.cornerRadius(4)
Text("실행 결과:")
.fontWeight(.medium)
Button("2씩 증가시키기") {
let incrementByTwo = makeIncrementer(by: 2)
incrementResult = [
incrementByTwo(),
incrementByTwo(),
incrementByTwo()
]
}
.buttonStyle(.bordered)
if !incrementResult.isEmpty {
Text("결과: \(incrementResult.map { String($0) }.joined(separator: ", "))")
.foregroundColor(.blue)
.padding(.top, 4)
Text("캡처된 total 변수가 호출마다 유지되어 2, 4, 6으로 증가합니다.")
.font(.caption)
.foregroundColor(.secondary)
}
}
}
}
#Preview {
ClosureFunctionDemoView()
}
import Foundation
// 예제 배열들
let ages = [15, 22, 19, 30, 17]
let scores = [40, 85, 75, 90]
// MARK: - 필터 예제: 나이 20 이상만 필터링
// 전체 타입 명시 + return 사용
let adults1 = ages.filter({ (age: Int) -> Bool in return age >= 20 })
print(adults1) // [22, 30]
// 타입 생략
let adults2 = ages.filter({ (age) in return age >= 20 })
print(adults2) // [22, 30]
// return 생략
let adults3 = ages.filter({ (age) in age >= 20 })
print(adults3) // [22, 30]
// 축약된 매개변수
let adults4 = ages.filter({ $0 >= 20 })
print(adults4) // [22, 30]
// 트레일링 클로저 문법
let adults5 = ages.filter { $0 >= 20 }
print(adults5) // [22, 30]
// MARK: - 정렬 예제: 내림차순 정렬
// 전체 타입 명시 + return 사용
let sorted1 = scores.sorted(by: { (a: Int, b: Int) -> Bool in return a > b })
print(sorted1) // [90, 85, 75, 40]
// 타입 생략
let sorted2 = scores.sorted(by: { (a, b) in return a > b })
print(sorted2) // [90, 85, 75, 40]
// return 생략
let sorted3 = scores.sorted(by: { (a, b) in a > b })
print(sorted3) // [90, 85, 75, 40]
// 축약된 매개변수 사용
let sorted4 = scores.sorted(by: { $0 > $1 })
print(sorted4) // [90, 85, 75, 40]
// 트레일링 클로저 문법
let sorted5 = scores.sorted { $0 > $1 }
print(sorted5) // [90, 85, 75, 40]
import SwiftUI
struct AdvancedClosureExampleView: View {
// MARK: - 상태 관리
@State private var squareResult = 0
@State private var uppercasedNames = ""
@State private var incremented: [Int] = []
@State private var fetchedData = ""
@State private var weakSelfMessage = ""
// MARK: - 일반 함수
func greetFunction(name: String) -> String {
return "Hello from function, \(name)"
}
// MARK: - 클로저
let greetClosure: (String) -> String = { name in
"Hello from closure, \(name)"
}
// MARK: - 축약형 클로저 (타입 명시)
let square: (Int) -> Int = { number in
return number * number
}
// MARK: - map 사용 예시
let names = ["Lee", "Kim", "Park"]
func performUppercase() {
uppercasedNames = names.map { $0.uppercased() }.joined(separator: ", ")
}
// MARK: - 변수 캡처 예시
func makeIncrementer(by amount: Int) -> () -> Int {
var total = 0
return {
total += amount
return total
}
}
func performIncrement() {
let incrementByThree = makeIncrementer(by: 3)
incremented = []
incremented.append(incrementByThree())
incremented.append(incrementByThree())
incremented.append(incrementByThree())
}
// MARK: - @escaping 클로저 예시
func fetchData(completion: @escaping (String) -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
completion("✅ Data loaded asynchronously")
}
}
func performFetch() {
fetchData { result in
self.fetchedData = result
}
}
// MARK: - 비동기 함수 예시 (struct에서는 weak self 불필요)
func loadSomethingAsynchronously(completion: @escaping () -> Void) {
DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
DispatchQueue.main.async {
self.weakSelfMessage = "✅ Loaded asynchronously"
completion()
}
}
}
var body: some View {
ScrollView {
VStack(spacing: 20) {
Group {
Text("클로저 기본 예제").font(.headline)
Text(greetFunction(name: "Taenee"))
Text(greetClosure("Taenee"))
}
Divider()
Group {
Text("단순 클로저 실행").font(.headline)
Button("🔢 Square 5 using closure") {
squareResult = square(5)
}
.buttonStyle(.bordered)
Text("5 squared = \(squareResult)")
}
Divider()
Group {
Text("고차함수 map 예제").font(.headline)
Button("🔤 Uppercase names") {
performUppercase()
}
.buttonStyle(.bordered)
if !uppercasedNames.isEmpty {
Text(uppercasedNames)
}
}
Divider()
Group {
Text("변수 캡처 예제").font(.headline)
Button("➕ Increment by 3") {
performIncrement()
}
.buttonStyle(.bordered)
if !incremented.isEmpty {
Text("Incremented: \(incremented.map { String($0) }.joined(separator: ", "))")
}
}
Divider()
Group {
Text("비동기 처리 예제").font(.headline)
Button("🌐 Fetch async data (@escaping)") {
performFetch()
}
.buttonStyle(.bordered)
if !fetchedData.isEmpty {
Text(fetchedData)
}
}
Divider()
Group {
Text("비동기 로딩 예제").font(.headline)
Button("🧠 Simulate async loading") {
loadSomethingAsynchronously { }
}
.buttonStyle(.bordered)
if !weakSelfMessage.isEmpty {
Text(weakSelfMessage)
}
}
}
.padding()
}
}
}
#Preview {
AdvancedClosureExampleView()
}
728x90
'iOS > Swift' 카테고리의 다른 글
[문법/키워드] mutating, Associated Value (연관 값) (0) | 2025.05.20 |
---|---|
Swift에서 closure 함수 쓸 때 사용되는 @escaping 은 뭘까? (0) | 2025.05.14 |
Swift 접근 제어 요약 (0) | 2025.05.02 |
내가 더 이상 헷갈리기 싫어서 적는 Swift의 핵심 구성 요소 (0) | 2025.05.01 |
Managing your app’s life cycle (0) | 2025.04.30 |