Closures - 2

Youn·2021년 8월 17일
0

iOS-Swift

목록 보기
4/11
post-thumbnail

Syntax Optimization - 문법 최적화

→ 코드 최대한 단순하게 축약

1) 파라미터와 리턴형 생략 가능
2) 파라미터 name 은 short-hand argument 이름(= $0) 으로 대치 (파라미터 이름과 in keyword 지움)
3) 단일 리턴문인경우 리턴 키워드 생략 (implicit return)
4) 파라미터가 마지막 파라미터라면 (trailing closure)
5) 파라미터 없으면 괄호 생략

var pro = products.filter({ (name: String) -> Bool in
	return name.contains("pro")
})
==
products.filter({ 
	$0.contains("Pro")
})
/*trailing closure */
products.filter () {
	$0.contains("Pro") //argument label만 남아있으면 지워도 됨
}
-- () 생략 
products.filter {
	$0.contains("Pro") //argument label만 남아있으면 지워도 됨
}

Capturing Values

→ nested function 은 값을 캡쳐함

"값을 캡쳐한다" : 값을 가져와서 쓴다. (복사본 캡쳐(objc) / 참조캡쳐, 원본그대로 가져옴(swift))
원본 값이 사라져도 클로저의 body 안 에서 그 값을 활용

var num = 0
let c = { print("#1: \(num)") } // closure 외부의 값에 접근, capture
c()
num += 1
print("#2: \(num)")
 
>> #1: 0 
>> #2: 1
  • 클로저 내부에서 외부 값 참조. 내부에서 값 바꾸면 외부값도 함께 바뀜
--- 내부에서 값 바꾸면 외부값도 바뀜---
var num = 0
let c = { num+=1 print("#1: \(num)") }
c()
print("#2: \(num)")

>> #1: 1 
>> #2: 1     (objc 에서는 0이 출력됨)
  • 메모리 관리안하면 참조사이클 문제 발생함
    클로저에서 값을 캡쳐하면 참조가 획득된다
  • 한 클로저를 두 상수나 변수에 할당하면 그 두 상수나 변수는 같은 클로저를 참조하고 있게 됨 참고
--- 하나의 클로저, 2개 할당 & 서로 다른 클로저 --- 
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
  var runningTotal = 0
  func incrementer() -> Int {
    runningTotal += amount
    return runningTotal
  }
  return incrementer
}

let plusTen = makeIncrementer(forIncrement: 10)
let plusSeven = makeIncrementer(forIncrement: 7)

// 함수가 각기 실행되어도 실제로는 변수 runnigTotal과 amount가 캡쳐되서 그 변수를 공유하기 때문에 누적된 결과를 가진다.
let plusedTen = plusTen() // 10
let plusedTen2 = plusTen() // 20
// 다른 클로저이기 때문에 고유의 저장소에 runningTotal과 amount를 캡쳐해서 사용한다.
let plusedSeven = plusSeven() // 7
let plusedSeven2 = plusSeven() // 14

Escaping Closure

클로저가 함수의 인자로 전달되지만 함수 밖에서 실행되는 것(함수가 반환된 후 실행되는 것)

  • Non-escaping closure
    • 클로저 파라미터는 기본적으로 non-escaping closure → 함수 실행 끝나기 전에 클로저 실행이 끝나야 함
  • Escaping closure
    • 클로저가 함수의 실행흐름을 벗어나더라도 메모리 에러없이 정상적으로 실행됨
--- unescaping closure --- 
func perform(closure: () -> ()) {
	print("start")
    closure()
    print("end")
}
perform {
    print("closure")
} //  == perform(closure:{ print("hihi") })
>> start
>> closure
>> end
// 함수 body 내에서 호출된 closure 는 항상 함수 실행이 종료되기 전에 실행이 완료됨
// 함수의 실행흐름을 탈출하지 X

--- escaping closure ---
func performEscaping(closure: () -> ()) {
   print("start")
   
   DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
      closure()
   }  // closure 는 non-escaping 이기 때문에 에러남
}
== fix! 
func performEscaping(closure: @escaping () -> ()) {
   print("start")
   
   DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
      closure()
   }  
   
   print("end")
}
performEscaping {
  print("closure")
}
>> start
>> end
>> closure ( 3초뒤 ) 
  • 시작시점과 종료시점이 특정되지 X
  • parameter 의 생명주기
    • 함수가 실행되면 생성, 함수의 실행이 끝나면 자동으로 제거
    • non-escaping 전달된 closure 는 함수의 실행이 끝나면 제거됨 -> 실행할 코드가 사라지는 것
  • @escaping을 사용하는 클로저에서는 self를 명시적으로 언급해야함
profile
youn

0개의 댓글