Closures
글로벌 스코프에서 단독으로 사용할 수 없음, 함수와 사용방법 등이 비슷하나 차이점은 arugumet label을 사용하지 않음
{(parameters) -> ReturnType in
statements
}
{ statements }
파라미터와 리턴이 없는 클로저 작성과 호출 예시
let c = { print("Hello, Swift") }
c() // Hello Swift
클로저를 상수 안에 넣는 예시
let c2 = { (str : String) -> String in
return "Hello, \(str)"
}
let result = c2("Closure")
print(result) // Hello, Closure
상수 안에 넣은 클로저를 arugumet로 전달, 클로저 자체를 직접 arugumet로 넣는 예시
arugumet에 직접 작성한 클로저를 inline 클로저라고 함 (클로저 바디에서 구현한 코드가 짧을 때 사용)
func perform(closure : (String) -> String {
print(closure("ios"))
}
perform(closure : c2) // Hello, ios
perform(closure : { (str : String) -> String in
return "Hi, \(str)" //Hi,ios
})
let products = ["MacBook Air", "MacBook Pro", "iMac", "iMac Pro", "Mac Pro", "Mac Mini",
"iPad Pro", "iPad", "iPad mini", "iPone Xs", "iPone Xr", "iPone 8", "iPone 7", "AirPods",
"Apple Watch Series 4", "Apple Watch Nike+"]
var proModels = products.filter({ (name : String) -> Bool in
return name.contains("Pro")
})
print(proModels) // "MacBook Pro", "iMac Pro", "Mac Pro", "iPad Pro"
proModel.sort(by: { (lhs : String, rhs : String) -> Bool in
return lhs.caseInsensitiveCompare(rhs) == .orderedAscending
})
print(proModels) //, "iMac Pro", "iPad Pro", "Mac Pro", "MacBook Pro"
Closures Syntax Optimization
스위프트는 단순한 문법을 추구하기 때문에 복잡한 클로저 문법을 최대한 단순하게 보정해주는 작업
1. 파라미터 형식과 리턴형 지우기
2. 파라미터 이름을 생략하고 Shorthand Augument Names으로 대체 (파라미터 이름과 in 키워드를 지운 후 return 문쪽에 파라미터 순서대로 &0, &1 순으로 대체)
3. 클로저에 포함된 코드가 단일 리턴문이라면 리턴 키워드 생략 -> Implicit Returns
4. 클로저가 마지막 파라미터라면 Trailing Closures로 작성 Augument Label이 남아있다면 Augument Label 삭제
5. 괄호 사이에 파라미터가 더 이상 없다면 괄호 생략
proModels = products.filter({ (name : String) -> Bool in
return name.contains("Pro")
// 1. 파라미터 형식과 리턴형 지우기
proModels = products.filter({ (name) in
return name.contains("Pro")
})
// 2. 파라미터 이름을 생략하고 Shorthand Augument Names으로 대체
// (파라미터이름과 in 키워드를 지운 후 return 문쪽에 파라미터 순서대로 &0, &1 순으로 대체)
proModels = products.filter({
return $0.contains("Pro")
})
// 3. 클로저에 포함된 코드가 단일 리턴문이라면 리턴키워드 생략 -> Implicit Returns
proModels = products.filter({
$0.contains("Pro")
})
// 4. 클로저가 마지막 파라미터라면 Trailing Closures로 작성 Augument Label이 남아있다면
// Augument Label 삭제
proModels = products.filter() {
$0.contains("Pro")
}
// 5. 괄호 사이에 파라미터가 더 이상 없다면 괄호 생략
proModels = products.filter {
$0.contains("Pro")
}
proModel.sort(by: {(lhs : String, rhs : String) -> Bool in
return lhs.caseInsensitiveCompare(rhs) == .orderedAscending
})
// 1. 파라미터 형식과 리턴형 지우기
proModel.sort(by: {(lhs, rhs) in
return lhs.caseInsensitiveCompare(rhs) == .orderedAscending
})
// 2. 파라미터 이름을 생략하고 Shorthand Augument Names으로 대체
// (파라미터이름과 in 키워드를 지운 후 return 문쪽에 파라미터 순서대로 &0, &1 순으로 대체)
proModel.sort(by: {
return $0.caseInsensitiveCompare($1) == .orderedAscending
})
// 3. 클로저에 포함된 코드가 단일 리턴문이라면 리턴키워드 생략 -> Implicit Returns
proModel.sort(by: {
$0.caseInsensitiveCompare($1) == .orderedAscending
})
// 4. 클로저가 마지막 파라미터라면 Trailing Closures로 작성 Augument Label이 남아있다면
// Augument Label 삭제
proModel.sort() { in
$0.caseInsensitiveCompare($1) == .orderedAscending
}
// 5. 괄호 사이에 파라미터가 더 이상 없다면 괄호 생략
proModel.sort { in
$0.caseInsensitiveCompare($1) == .orderedAscending
}
Capturing Values
클로저 내부에서 클로저 외부에 있는 값에 접근할 때 참조를 선택함, 내부에서 가져온 값을 변경하면 외부의 값도 같이 변경
Escaping Closures
함수의 실행흐름과 관련 없이 실행 가능하게 하는 문법
Escaping Closures | Non escaping Closures |
함수 실행 흐름과 상관없이 클로저를 실행 | 함수가 끝나기 전에 클로저를 실행이 끝나야 함 |
func performEscaping(closure : @escaping () -> ()) {
print("start")
DispatchQueue.main.asyncAfter(deadline : .now() + 3) {
closure()
}
print("end")
}
performEscaping {
print("closure")
}
위 예시는 3초뒤에3초 뒤에 closure 가 실행되는 문법인데 함수는 실행이 완료되면 제거되게 되는데 이때 클로저는 3초 뒤에 실행되기 전에 제거돼버림 (클로저는 기본 Non escaping Closures), 제거되는 것을 막기 위해 Escaping Closures로 변환하면클로저의 실행이 완료되기 전까지 클로저를 제거하지 않음