[Swift] 클로저

김태형·2023년 3월 24일
0

Swift

목록 보기
14/22

이번 내용은 클로저에 관한 내용이다. 책으로만 읽었을 때는 감이 잘 안와서, 여러 영상과 블로그를 찾아보고 나름대로 정리한 것이다.

스위프트는 객체 지향형 프로그래밍에 함수형 프로그래밍 패러다임을 더한 언어이다. 함수형 프로그래밍 패러다임 중 가장 먼저 알아야 하고, 스위프트를 배운다면 무조건 마주치게 될 것이 바로 '클로저'이다.



클로저

클로저는 어떤 테스크를 수행하기 위한 코드 블록을 말한다. 그럼 함수랑 다른 점이 있는가? 라고 생각을 해보면 사실 함수가 클로저의 일부이다 !

이름 있는 클로저 -> 함수
이름 없는 클로저 -> 일반적으로 클로저라고 부르는 형태

클로저는 매개변수와 반환 값의 타입을 생략할 수 있음

  • 파마리터를 받을 수 있음
  • 특정값을 반환할 수도 있음
  • 함수 파라미터로 받을 수도 있음
    • 클로저를 따로 만들어 놓지 않고, 함수 수행 시 바로 작성 가능
  • 축약된 전달인자 이름 사용 가능
  • 후행 클로저 (trailing closure) 문법 사용 가능
{ (parameters) -> return type in
	statements
}

  • in을 기준으로 클로저 선언부클로저 실행부가 나뉨
    • 클로저 선언부 : 파라미터와 리턴타입을 명시해줌
    • 클로저 실행부 : 실행 코드를 작성하는 곳
  • Swift에서는 클로저와 함수를 타입으로 사용할 수 있음 (일급 시민)
    • 일급 시민이기 때문에 변수에 할당할 수 있고, 다른 함수 파라미터로 전달할 수도 있음
let checking = { print("checking!!") }
checking()    //checking!!



let checking = { (id: String) in    //파라미터를 받을 수 있음
	print("checking id: \(id)")
}
checking("User1")    //checking id: User1



let checking = { (id: String) -> Bool in    //리턴값 존재
	if id == "User1" {
		return false
	} 
	return true
}

let isValid = checking("User2")    //true
let isValid = checking("User1")    //false



func validate(id: String, checking: (String) -> Bool) -> Bool {    //위에 작성한 checking 클로저를 파라미터로 사용함
	let isValid = checking(id)
	return isValid
}

let validationResult = validate(id: "User2", checking: checking)    //true (리턴 값이 true임)





let validationResult2 = validate(id: "User1", checking: { (id: String) -> Bool in
	if id == "User0" {
		return false
	}
	return true
})    //checking에 대한 클로저를 따로 정의하지 않고, 함수 안에서 만들었음


doSomeClosure ({ print("Hello World") }    // 클로저를 안에서 만들고 바로 사용

클로저 표현 간소화

  • 클로저는 생략이나 짧게 만들 수 있음
  • 선언부를 줄이거나 in을 없앨 수도 있음
  • 맨 마지막에 들어오는 클로저는 파라미터를 작성해주지 않아도 됨 (trailing closure)
  • 하지만 너무 줄이면 코드의 가독성이 떨어질 수도 있기 때문에 적절하게 줄여줘야 함!

종류

  • 문맥을 이용한 타입 유추
    • 매개변수의 타입이나 반환 값의 타입을 굳이 표현해주지 않고 생략하더라도 문제가 없음
  • 단축 인자 이름
    • 첫 번째 전달인자부터 $0, $1, $2, … 순서로 표현 가능
      • 단축 인자 표현을 사용하면 선언부와 실행부를 나누기 위한 in을 사용할 필요도 없어짐
  • 암시적 반환 표현
    • return 키워드 생략 가능
let validationResult = validate(id: "User0", checking: { (id: String) -> Bool in
	if id == "User1" {
		return false
	}
	return true
})



let validationResult2 = validate(id: "User1", checking: { id in    //클로저 선언부를 줄일 수 있음 (return이 있기 때문에 스위프트에서 알아서 추론함)
	if id == "User0" {
	return false
	}
	return true
})




let validationResult3 = validate(id: "User1", checking: { id in    //if문을 리팩토링함
	let isValide = id != "User0"
	return isValid
})



let validationResult4 = validate(id: "User1", checking: { $0 != "User0" })    //$0, $1, ... : 클로저로 들어오는 input을 나타냄


let validationResult5 = validate(id: "User1") { $0 != "User0" }    //trailing closure: 맨 마지막에 들어오는 클로저는 파라미터를 작성하지 않아도 됨

값 획득

  • 자신이 정의된 위치의 주변 문맥을 통해 상수나 변수를 획득(Capture)할 수 있음
  • 클로저는 주변에 정의한 상수나 변수가 존재하지 않더라도 해당 상수나 변수의 값을 자신 내부에서 참조하거나 수정할 수 있음
  • 클로저는 비동기 작업에 많이 사용되는데, 클로저를 통해 비동기 콜백을 작성하는 경우, 현재 상태를 획득해두지 않으면, 클로저의 기능을 실행하는 순간에 메모리에 존재하지 않는 경우가 발생할 수 있음

참조 타입

  • 클로저는 참조 타입임 → 값 획득을 통해 변수를 증가시킬 수 있음
  • 함수나 클로저를 할당할 때 사실은 참조를 설정하는 것임
  • 상수에 클로저를 할당한다는 것은 값을 할당하는 것이 아니라 해당 클로저의 참조를 할당하는 것임!

탈출 클로저 (@escaping)

  • 함수의 전달인자로 전달한 클로저가 함수 종료 후에 호출될 때 클로저가 함수를 탈출한다고 표현함
  • 클로저를 매개변수로 갖는 함수를 선언할 때 매개변수 이름의 콜론(:) 뒤에 @escaping 키워드를 통해 클로저가 탈출하는 것을 허용
  • 클로저의 기본값은 비탈출 클로저임
    • 비탈출 클로저는 함수 내부 스코프 안에서만 사용 가능
    • 외부 상수, 변수에 저장 불가능
  • 함수 외부에 정의된 변수나 상수에 저장되어 함수가 종료된 후에 사용할 경우에 탈출 클로저 사용 ex. 비동기 작업할 때 캠플리션 핸들러를 전달인자를 이용해 클로저 형태로 받는 함수들이 있음 → 함수가 작업을 종료하고 난 이후(return 이후)에 컴플리션 핸들러인 클로저를 호출하기 때문에 클로저는 함수를 탈출해 있어야만 함
  • 탈출 클로저를 사용한다면
    • 해당 클로저를 외부 변수, 상수에 저장 가능
    • 해당 함수가 끝나서 리턴된 이후에도 클로저 실행 가능
var sampleClosure: () -> Void = { }

func callback(closure: @escaping() -> Void) {    //만약 @escaping이 없다면 에러 발생
	sampleClosure = closure
	sampleClosure()
}

0개의 댓글