[iOS 7주차] Swift: Type Casting, do-catch, throw, Error

DoyleHWorks·2024년 12월 4일
1

What I've learned:

Type Casting

Swift에서 타입캐스팅(type casting)은 인스턴스를 다른 타입으로 변환하거나 확인하는 방법이다. 이는 클래스 계층 구조에서 주로 사용되며, 특정 타입의 인스턴스인지 확인하거나, 더 상위 또는 하위 타입으로 변환할 때 사용된다.

타입캐스팅에 사용되는 키워드:
1. is
2. as (as?, as! 포함)


1. is

특정 인스턴스가 해당 타입에 속하는지 확인한다. is는 Boolean 값을 반환한다.

let someValue: Any = "Hello, Swift!"

if someValue is String {
    print("someValue는 String 타입입니다.")
}

2. as

타입 변환에 사용되며, 세 가지 형태가 있다:

(1) as

  • 컴파일러가 컴파일 시점에 확실히 알 수 있는 경우 사용됨
  • 주로 프로토콜 캐스팅에 사용됨
protocol Greetable {
    func greet()
}

class Person: Greetable {
    func greet() {
        print("Hello!")
    }
}

let person = Person()
let greeter: Greetable = person as Greetable // 명시적 프로토콜 캐스팅

(2) as? (Optional Casting)

  • 안전한 타입 변환을 시도하며, 변환에 실패하면 nil을 반환함
  • 주로 타입 변환 가능성을 알 수 없는 경우 사용됨
let unknownValue: Any = "I am a string"
if let stringValue = unknownValue as? String {
    print("Casting 성공: \(stringValue)")
} else {
    print("Casting 실패")
}

(3) as! (Forced Casting)

  • 강제 타입 변환을 시도하며, 실패할 경우 런타임 오류를 발생시킴
  • 변환이 확실하다고 판단될 때만 사용해야 함
let anotherValue: Any = 42
let integerValue = anotherValue as! Int // 강제 변환
print("변환된 값: \(integerValue)")

3. 클래스 계층 구조와 타입캐스팅

클래스 계층에서 상위 타입과 하위 타입 간 변환에 자주 사용된다.

class Animal {
    func makeSound() {
        print("Animal sound")
    }
}

class Dog: Animal {
    func bark() {
        print("Woof!")
    }
}

let myAnimal: Animal = Dog() // 업캐스팅
myAnimal.makeSound()

// 다운캐스팅
if let myDog = myAnimal as? Dog {
    myDog.bark()
}
  • 업캐스팅(Upcasting): 하위 클래스의 인스턴스를 상위 클래스 타입으로 변환 (암시적으로 가능)
  • 다운캐스팅(Downcasting): 상위 클래스의 인스턴스를 하위 클래스 타입으로 변환 (명시적으로 필요)



do, catch, try

Swift의 do-catch 구문은 에러 처리(Error Handling)를 위한 구문으로, 오류가 발생할 가능성이 있는 코드를 안전하게 실행하고 오류를 처리하는 데 사용된다.

do-catch 구문의 기본 구조는 다음과 같다:

do {
    // 에러가 발생할 가능성이 있는 코드
} catch {
    // 에러를 처리하는 코드
}

기본 구조와 사용 방법

do {
    let result = try someThrowingFunction() // 에러를 던질 수 있는 함수 호출
    print("Result: \(result)")
} catch {
    print("An error occurred: \(error)")
}

주요 키워드

  • do: 오류가 발생할 가능성이 있는 코드를 포함하는 블록
  • try: 에러를 던질 가능성이 있는 함수 호출 전에 사용
  • catch: 발생한 에러를 처리하는 블록. 다양한 조건에 따라 여러 catch 블록을 정의할 수 있음

여러 catch 블록 처리

여러 종류의 에러를 구분하여 처리할 수 있다.

enum MyError: Error {
    case invalidInput
    case networkError
}

func throwingFunction(_ value: Int) throws -> String {
    if value < 0 {
        throw MyError.invalidInput
    } else if value == 0 {
        throw MyError.networkError
    }
    return "Success"
}

do {
    let result = try throwingFunction(0)
    print(result)
} catch MyError.invalidInput {
    print("Invalid input error")
} catch MyError.networkError {
    print("Network error occurred")
} catch {
    print("Unknown error: \(error)")
}

에러를 무시하는 방법

에러를 무시하고 처리하지 않을 때는 try?를 사용할 수 있다.

let result = try? throwingFunction(1) // 에러 발생 시 nil 반환
print(result ?? "No result")

강제로 에러를 무시하는 방법

try!를 사용하면 에러가 발생하지 않는다는 것을 확신할 때 사용한다. 하지만 실제로 에러가 발생하면 런타임 에러가 발생한다.

let result = try! throwingFunction(1) // 에러 발생 시 앱 크래시
print(result)

간단한 예제

func divide(_ a: Int, by b: Int) throws -> Int {
    if b == 0 {
        throw NSError(domain: "DivideByZero", code: 1, userInfo: nil)
    }
    return a / b
}

do {
    let result = try divide(10, by: 0)
    print("Result: \(result)")
} catch {
    print("Error: \(error)")
}



throws, throw

Swift에서 throwsthrow는 에러 처리에 사용되며, 서로 다른 역할을 한다.


throws

  • 정의: 함수나 메서드가 에러를 발생시킬 수 있음을 선언하는 키워드

  • 위치: 함수의 시그니처에 사용

  • 의미: 이 함수를 호출할 때 에러를 처리해야 함

  • 예시:

    func canThrowError() throws {
        // 에러를 던질 가능성이 있는 코드
    }
  • 호출 시에는 try 키워드가 필요:

    do {
        try canThrowError()
    } catch {
        print("Error caught: \(error)")
    }

throw

  • 정의: 실제로 에러를 발생시킬 때 사용하는 키워드

  • 위치: do 블록이나 함수 내부에서 사용

  • 의미: 선언된 에러를 발생시킴

  • 예시:

    enum MyError: Error {
        case somethingWentWrong
    }
    
    func throwErrorExample() throws {
        throw MyError.somethingWentWrong
    }
  • 위의 함수는 호출 시 다음과 같이 에러를 던진다:

    do {
        try throwErrorExample()
    } catch {
        print("Caught an error: \(error)")
    }

요약

키워드역할사용 위치
throws함수가 에러를 던질 수 있음 선언함수 시그니처
throw에러를 실제로 던짐함수/메서드 내부

예제

enum DivisionError: Error {
    case divideByZero
}

func divide(_ a: Int, by b: Int) throws -> Int {
    if b == 0 {
        throw DivisionError.divideByZero // 실제 에러 발생
    }
    return a / b
}

do {
    let result = try divide(10, by: 0) // throws로 선언된 함수 호출
    print(result)
} catch {
    print("Error: \(error)") // throw로 발생한 에러를 처리
}



Error

Swift의 Error 타입은 에러를 표현하고 처리하는 표준 프로토콜이다. 사용자 정의 에러 타입을 만들거나 Swift에서 제공하는 기본 에러를 사용할 때 기반이 되는 구조이다.


Error 프로토콜

Error는 빈 프로토콜로, 특정 규칙을 강제하지 않는다. 대신, Swift에서 에러를 던지고 처리하기 위해 이 타입을 따르는 객체로 정의해야 한다.

protocol Error {}

이 프로토콜을 채택하면 에러로 사용할 수 있는 타입을 만들 수 있다.


사용 예시: 커스텀 에러 타입

주로 열거형(enum)을 사용하여 에러를 정의한다. 열거형은 에러의 다양한 상태를 명확하게 표현할 수 있기 때문이다.

enum MyError: Error {
    case invalidInput
    case networkFailure(code: Int)
    case unknown
}

에러 처리 흐름

  1. 에러를 정의: Error를 준수하는 타입으로 에러 정의
  2. 에러를 던짐: throw를 사용하여 에러 발생
  3. 에러를 처리: do-catch 구문을 사용하여 에러 처리

Error 타입의 활용

1. 에러의 추가 정보 제공

Error 타입에 연관 값을 추가하여, 발생한 에러에 대한 추가 정보를 전달할 수 있다.

enum NetworkError: Error {
    case timeout
    case serverError(message: String)
    case unknown
}

func fetchData() throws {
    throw NetworkError.serverError(message: "Internal Server Error")
}

do {
    try fetchData()
} catch NetworkError.serverError(let message) {
    print("Server Error: \(message)")
} catch {
    print("An error occurred: \(error)")
}

2. CustomStringConvertible과의 결합

Error 타입을 사람이 읽기 쉬운 메시지로 변환하려면 CustomStringConvertible을 채택한다.

enum FileError: Error, CustomStringConvertible {
    case fileNotFound(String)
    case unreadable(String)
    
    var description: String {
        switch self {
        case .fileNotFound(let fileName):
            return "File '\(fileName)' not found."
        case .unreadable(let fileName):
            return "File '\(fileName)' cannot be read."
        }
    }
}

do {
    throw FileError.fileNotFound("data.txt")
} catch {
    print(error) // File 'data.txt' not found.
}

Swift 내장 에러 타입

Swift에서는 몇 가지 기본 제공 에러 타입이 있다.

  1. NSError: Objective-C와 상호작용할 때 사용. 주로 Foundation 프레임워크에서 발생
  2. DecodingError, EncodingError: JSON 인코딩/디코딩에서 발생
  3. URLError: URL 요청과 관련된 에러
profile
Reciprocity lies in knowing enough

0개의 댓글