멋쟁이사자처럼 앱스쿨 1기

[멋쟁이사자처럼] 앱스쿨 1기 - 클로저&이스케이프 클로저 (14일차 22.10.05)

It’s me. Right. 2022. 10. 6. 09:03
반응형

Swift 5.7버전이 발표되면서 새로 갱신된 기능에 대한 소개를 받았다.

 

<if let(var) 후 신규 변수명 생성 생략>

// 옵셔널이 되면 nil 값이 될 수도 있고 아닐 수도 있다
var name: String?

if let myName = name {
    print("\(myName)")
} else {
    print("nil입니다")
}

name = "ned"

if let name = name {
    print("\(name)")
} else {
    print("nil입니다")
}

// Swift 5.7의 신기능!
if let name {
    print("\(name)")
} else {
    print("nil입니다")
}

<???>

func sayHello(name: String?) {
    guard let name else {
        return print("nil입니다")
    }
    
    print("Hello World!")
}

sayHello(name: "ned")
sayHello(name: nil)

func sayHello(name: String?) {
    guard let _ = name else {
        return print("nil입니다")
    }
    
    print("Hello World!")
}

sayHello(name: "ned")
sayHello(name: nil)

1. 오전일정 : 클로저

클로저

  • 함수와 역할은 같지만 생긴 구조가 다르다. 함수를 결과(return)으로 반환한다.
  • 아래 코드에서 functionA는 functionB라는 이름의 함수( () -> Int )를 반환한다.
func functionA() -> () -> Int {
	var counter = 0 
    
    func functionB() -> Int {
    	return counter + 10
    }
	return functionB
}

let myClosure = functionA()
let result = functionB()
// 간단한 클로저 예제

func functionA() -> () -> Int {
    
    var counter = 5
    
    // functionA 함수 안에 functionB 함수 선언 - 중첩된 함수
    func functionB() -> Int {
        // functionB 밖의 counter를 사용한 계산값을 반환한다
        return counter + 30
    }
    
    let result = functionB()
    print("\(result)")
    
    // functionB() 이것은 functionB를 호출하는 문구
    // functionB   이것은 functionB 함수 그 자체를 지칭함
    return functionB
}

// functionA() 호출의 결과는 functionB 함수이고
// functionB 함수 안에는 counter값이 계속 붙잡혀 활용이 된다
// myClosure가 곧 functionB이기 때문에 myClosure가 존재하는 동안 counter도 붙잡혀 존재한다
let myClosure = functionA()
print("\(myClosure())")
  • map, reduce, filter, sorted 기능을 클로저를 통해서 확장을 할 수 있음
  • Swift의 클로저는 다른 프로그래밍 언어의 람다와 유사함
  • 클로저는 참조타입이다.(Class 작성과 같이 동작함) 따라서 참조가 없는 다른 클로저는 만들려면 새로 생성해야 한다.
/ 캡처값 예제
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
    var runningTotal: Int = 0
    
    func incrementer() -> Int {
        runningTotal += amount
        return runningTotal
    }
    
    return incrementer
}

// 생성된 반환되는 함수는 runningTotal = 0,amount = 10으로 캡쳐되어 작동
let increFuncTen = makeIncrementer(forIncrement: 10)

// 생성된 반환되는 함수는 runningTotal = 0,amount = 7로 캡쳐되어 작동
let increFuncSeven = makeIncrementer(forIncrement: 7)

print("\(increFuncTen())") // 0 + 10 = 10
print("\(increFuncSeven())") // 0 + 7 = 7

print("\(increFuncTen())") // 10 + 10 = 20
print("\(increFuncSeven())") // 7 + 7 = 14

print("\(increFuncTen())") // 20 + 10 = 30
print("\(increFuncSeven())") // 14 + 7 = 21


// 두 함수의 관계는 참조 타입이라는 걸 알아보려 한다.
// 동일한 runningTotal 값을 공유하고 있다.
let alsoIncreFuncTen = increFuncTen

print("\(increFuncTen())") // 30 + 10 = 40
print("\(alsoIncreFuncTen())") // 0 + 10 = 10 ? -> 실제로는 50
print("\(increFuncTen())") // 40 + 10 = 50 ? -> 실제로는 60

// 필요하면 새로 만들어야...
let myIncreFuncTen = makeIncrementer(forIncrement: 10)
print("\(myIncreFuncTen())") // 0 + 10 = 10

 

정렬 메서드

  • Swift 표준 라이브에서 sorted(by:) 메서드 제공함
  • 기존 배열은 sorted(by:)로 수정되지 않고, 정렬된 요소를 새로운 배열로 반환함
  • sorted(by:)에서 by의 매개변수로 클로저가 들어가야 한다. 
    ex) sorted(by: (self.String , self.String) -> Bool) 
let names1 = ["one", "two", "three", "four", "five"]
var defualtSorted = names1.sorted(by: >)               //내림차순 출력, '>' 가 최소한의 클로저 표현식이다
print(defualtSorted)                         

func backward(s1:String, s2:String) -> Bool {
	return s1 > s2
}

let names2 = ["one", "two", "three", "four", "five"]
var reverseSorted = names2.sorted(by: backward)
print(reverseSorted)               //내림차순 출력


//해당 정렬 클로저는 (String, String) -> Bool 타입의 함수를 필요로 함
  • 클로저 표현식 예시.... 이거 음... 화이팅...?
// 배열 정렬 예제
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
var defaultNames = names.sorted()
print(defaultNames)


// 정렬 기준을 만들어주는 함수 만들기
func backward(_ s1: String, _ s2: String) -> Bool {
    print("\(s1)과 \(s2)를 비교합니다")
    return s1 > s2
}

var reverseNames = names.sorted(by: backward)
print(reverseNames)

//전역함수 backwarddhk 똑같이 작동하는 클로저 표현식
//인라인 클로저라는 구현법으로 by: 에 바로 써서 보낸다
reverseNames = names.sorted(by: { (_ s1: String, _ s2: String) -> Bool in
	return s1 > s2
})
print(reverseNames)

//한줄로 가능
reverseNames = names.sorted(by: { (_ s1: String, _ s2: String) -> Bool in return s1 > s2 })
print(reverseNames)

//더 짧게
//배열이 String으로 채워진걸 아니까... 타입추론...?
reverseNames = names.sorted(by: { (_ s1, _ s2) -> Bool in return s1 > s2 })
print(reverseNames)

//더더 짧게
//정렬 클로저는 메서드에 인자로 전달되기 때문에 Swift는 파라미터 타입과 반환 값의 타입을 유추할 수 있다.
reverseNames = names.sorted(by: { s1, s2 in return s1 > s2 })
print(reverseNames)

//더더더 짧게
//ㅇㅅㅇ
reverseNames = names.sorted(by: { s1, s2 in s1 > s2 })
print(reverseNames)

//더더더더 짧게
//짧은 인자이름으로 대체 ㅇㅅㅇ??
reverseNames = names.sorted(by: { $0 > $1 })    //첫번째, 두번째 요소값을 사용하는 Swift 약속


//더더더더더 짧게
//연산자 메소드 사용 ㅇㅅㅇ;;
reverseNames = names.sorted(by: >)

/*
 Alex과 Chris를 비교합니다
 Ewa과 Alex를 비교합니다
 Ewa과 Chris를 비교합니다
 Barry과 Alex를 비교합니다
 Barry과 Chris를 비교합니다
 Daniella과 Alex를 비교합니다
 Daniella과 Barry를 비교합니다
 Daniella과 Chris를 비교합니다
 Daniella과 Ewa를 비교합니다
 ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
 */

2. 오후일정 : 이스케이프 클로저 & Objective-C

이스케이프 클로저

// 이스케이프 클로저 (Escaping Closures) 예제
var completionHandlers: [() -> Void] = []

func someFunctionWithEscapingClosures(completionHandler: @escaping () -> Void ) {
    completionHandlers.append(completionHandler)
}


func someFunctionWithNonescapingClosures(closure: () -> Void) {
    closure()
}

class SomeClass {
    var x = 0
    func doSomething() {
        someFunctionWithEscapingClosures { () -> Void in
            print("Hello")
            self.x = 100
        }
        someFunctionWithNonescapingClosures { () -> Void in
            print("World")
            x = 200
        }
    }
}

let instance = SomeClass()
instance.doSomething()
print("someFunctionWithNonescapingClosures - x : \(instance.x)") // 당연히 200

if let completionHandler = completionHandlers.first {
    completionHandler()
    print("someFunctionWithEscapingClosures - x : \(instance.x)") // 200이 아니라 100
}

 

Objective-C 미리보기 (내일 배울거 소개)


3. 오늘의 리뷰

맨날 블로그 다 써놓고 등록을 안하는 이유는 뭘까... 빠른 퇴근을 원하기 때문인가....ㅎㅎ

 

반응형