Today I Learned

23년 7월 21일 TIL

jjjkh 2023. 7. 21. 19:44

클로저

-클로저는 이름없는 함수로 코드블럭을 지칭한다.

-클로저는 상수나 변수의 참조를 캡처해 저장할 수 있다.

-클로저를 사용하는 이유는 기능을 저장하기 위해서 사용한다.

-클로저는 비동기 처리가 필요할 때 사용할 수 있는 코드블럭이다.

* 비동기처리란 ? = 특정 로직의 실행이 끝날때까지 기다려주지 않고 나머지 코드를 먼저 실행하는것을 비동기 처리라고 한다.

-클로저는 함수와 마찬가지로 참조타입이다.

- 예시 코드
    
    ```swift
    { (parameters) -> return type in
        // 구현 코드
    }
    
    // 함수와 클로저 비교
    func pay(user: String, amount: Int) {
        // code
    }
    
    let payment = { (user: String, amount: Int) in
        // code
    }
    ```
    
    ```swift
    // return 값 없는 클로저
    let driving = { (place: String) in // place는 매개변수
        print("\(place)으로 향하는 중입니다.")
    }
    driving("사무실")
    // 출력값: 사무실으로 향하는 중입니다.
    
    // return 값 있는 클로저
    let payment = { (user: String) -> Bool in
        print("\(user)님이 계산하셨습니다.")
        return true
    }
    payment("부장님")
    // 출력값: 부장님이 계산하셨습니다.
    
    ```

이스케이핑 클로저 (escaping closure)

-이스케이핑 클로저는 클로저가 메서드의 인자로 전달 되었을 때, 메서드의 실행이 종료된 후 실행되는 클로저입니다.

- 이경우 파라미터 타입 앞에 @escaping이라는 키워드를 명시해야합니다.

 * 예를들어 비동기로 실행되거나 completionhandler로 사용되는 클로저의 경우!

- 클로저를 메서드의 파라미터로 넣을 수 있습니다.

참고 자료

- https://docs.swift.org/swift-book/documentation/the-swift-programming-language/closures/

 

Documentation

 

docs.swift.org

 

 

예외 처리 

- 실패 가능한 상황과 예외처리

 * 에러처리

- 프로그램에서 에러가 발생한 상황에 대응하고 이에 대응하는 과정입니다.

-Swift에서는 런타임 에러가 발생한 경우, 이를 처리를 지원하는 클래스를 제공합니다.

-프로그램에서 모든 기능이 개발자가 예상하고 원하는대로 동작한다는 보장이 없기 때문에 예외처리를 통해 예외 상황을 구별하고 프로그램 자체적으로 오류를 해결하거나, 사용자에게 어떤 에러가 발생했는지 알려주는 등에 대한 조치와 대응을 해야합니다.

*Error

- Error는 던져질 수 있는 오류 값을 나타내는 유형을 말합니다.

- Error 프로토콜을 채택하여 사용자 정의 에러를 정의하여 사용할 수 있다

참고자료

-https://developer.apple.com/documentation/swift/error

 

Error | Apple Developer Documentation

A type representing an error value that can be thrown.

developer.apple.com

 

throw와 do-catch문 try문

* throw와 throw

-throw는 리턴 값을 반환하기 전에 오류가 발생하면 에러 객체를 반환한다는 의미이다.

-throw는 오류가 발생할 가능성이 있는 메소드 제목 옆에 써줍니다.

-throw는 오류가 발생할 구간에 써줍니다.

 

//예시!!!

// 표현
func canThrowErrors() throws -> String
func cannotThrowErrors() -> String

// 예시
struct Item {
    var price: Int
    var count: Int
}

enum VendingMachineError: Error {
    case invalidSelection
    case insufficientFunds(coinsNeeded: Int)
    case outOfStock
}

let favoriteSnacks = [
    "Alice": "Chips",
    "Bob": "Licorice",
    "Eve": "Pretzels",
]

func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) throws {
    let snackName = favoriteSnacks[person] ?? "Candy Bar"
    try vendingMachine.vend(itemNamed: snackName)
}

class VendingMachine {
    var inventory = [
        "Candy Bar": Item(price: 12, count: 7),
        "Chips": Item(price: 10, count: 4),
        "Pretzels": Item(price: 7, count: 11)
    ]
    var coinsDeposited = 0

    func vend(itemNamed name: String) throws { // 👀
        guard let item = inventory[name] else {
            throw VendingMachineError.invalidSelection
        }

        guard item.count > 0 else { 
            throw VendingMachineError.outOfStock // 👀
        }

        guard item.price <= coinsDeposited else {
            throw VendingMachineError.insufficientFunds(coinsNeeded: item.price - coinsDeposited) // 👀
        }

        coinsDeposited -= item.price

        var newItem = item
        newItem.count -= 1
        inventory[name] = newItem

        print("Dispensing \(name)")
    }
}

var vendingMachine = VendingMachine()
vendingMachine.coinsDeposited = 8
do {
    try buyFavoriteSnack(person: "앨리스", vendingMachine: vendingMachine) // 👀
    print("성공! 냠.")
} catch VendingMachineError.invalidSelection {
    print("선택 불가한 항목입니다")
} catch VendingMachineError.outOfStock {
    print("재고가 없습니다.")
} catch VendingMachineError.insufficientFunds(let coinsNeeded) {
    print("금액이 부족합니다.. \(coinsNeeded) 코인을 추가로 넣어주세요.")
} catch {
    print("앗 예상치 못한 에러가 발생했습니다! 에러 메시지: \(error).")
}
// 출력값: 금액이 부족합니다... 4 코인을 추가로 넣어주세요.

func nourish(with item: String) throws {
    do {
        try vendingMachine.vend(itemNamed: item)
    } catch is VendingMachineError {
        print("자판기에서 구매할 수 없습니다.")
    }
}

do {
    try nourish(with: "소고기맛 과자")
} catch {
    print("예상치 못한 자판기와 관련없는 에러가 발생했습니다. 에러 메시지: \(error)")
}
// 출력값: 자판기에서 구매할 수 없습니다.

// catch에서 여러 에러 케이스를 한꺼번에 처리할 수 있습니다.
func eat(item: String) throws {
    do {
        try vendingMachine.vend(itemNamed: item)
    } catch VendingMachineError.invalidSelection, VendingMachineError.insufficientFunds, VendingMachineError.outOfStock {
        print("유효하지 않는 선택지이거나 재고가 없거나 금액이 부족합니다.")
    }
}

try? 와 try! 

* try? 

-do-catch 구문없이도 사용 가능하다.

-에러 발생시 nil값을 반환한다.

-에러가 발생하지 않으면 리턴 값의 타입은 옵셔널로 반환된다.

 

*  try! 

- 에러가 발생을 하면 앱이 강제 종료된다.

- 반환 타입은 옵셔널 언래핑된 값이 리턴된다.

- 오류가 발생하지 않는다는 보장아래 사용하여야한다.

 

'Today I Learned' 카테고리의 다른 글

23년 7월 25일 TIL  (0) 2023.07.25
23년 7월 24일 TIL  (0) 2023.07.24
23년 7월 20일 TIL  (0) 2023.07.20
23년 7월 19일 TIL  (0) 2023.07.19
23년 7월 18일 TIL  (0) 2023.07.19