[Swift][공식문서 털기] - Enumerations and Structures, Concurrency, Protocols and Extensions , Error Handling , Generics

dongle·2022년 12월 24일

Enumerations and Structures(열거형 및 구조)

enum을 사용하여 열거형을 만듭니다.
클래스 및 기타 모든 명명된 유형과 마찬가지로 열거형에는 연결된 메서드가 있을 수 있습니다.

enum Rank: Int {
    case ace = 1
    case two, three, four, five, six, seven, eight, nine, ten
    case jack, queen, king

    func simpleDescription() -> String {
        switch self {
        case .ace:
            return "ace"
        case .jack:
            return "jack"
        case .queen:
            return "queen"
        case .king:
            return "king"
        default:
            return String(self.rawValue)
        }
    }
}
let ace = Rank.ace
let aceRawValue = ace.rawValue

기본적으로 Swift는 0에서 시작하여 매번 1씩 증가하는 초기 값을 할당하지만 명시적으로 값을 지정하여 이 동작을 변경할 수 있습니다.
위 예시에서 Ace라는 초기 값이 명시적으로 지정 1되고 나머지 초기 값은 순서대로 할당됩니다.
문자열 또는 부동 소수점 숫자를 열거형의 원시 유형으로 사용할 수도 있습니다.
열거 사례의 원시 값에 액세스하려면 rawValue 속성을 사용하십시오.
init?(rawValue:)이니셜라이저를 사용하여 원시 값에서 열거형 인스턴스를 만듭니다. 원시 값과 일치하는 열거형 사례를 반환하거나 nil과 Rank가 일치 하지 않는 경우 반환합니다.

if let convertedRank = Rank(rawValue: 3) {
    let threeDescription = convertedRank.simpleDescription()
}

열거형의 케이스 값은 원시 값을 작성하는 또 다른 방법이 아니라 실제 값임으로 실제로 의미 있는 원시 값이 없는 경우 제공할 필요가 없습니다.

enum Suit {
    case spades, hearts, diamonds, clubs

    func simpleDescription() -> String {
        switch self {
        case .spades:
            return "spades"
        case .hearts:
            return "hearts"
        case .diamonds:
            return "diamonds"
        case .clubs:
            return "clubs"
        }
    }
}
let hearts = Suit.hearts
let heartsDescription = hearts.simpleDescription()

위에서 열거형 의 대소문자가 참조되는 두 가지 방법은 상수 hearts에 값을 할당할 때 hearts열거형 대소문자 Suit.hearts는 상수에 지정된 명시적 유형이 없기 때문에 전체 이름으로 참조됩니다.
스위치 내부에서 열거형 케이스는 .hearts의 값 self이 이미 슈트로 알려져 있기 때문에 축약형으로 참조합니다.
값의 유형이 이미 알려진 경우 언제든지 약식 형식을 사용할 수 있습니다.
열거형에 원시 값이 있는 경우 해당 값은 선언의 일부로 결정됩니다. 즉, 특정 열거 사례의 모든 인스턴스는 항상 동일한 원시 값을 가집니다.
열거 사례에 대한 또 다른 선택은 사례와 연결된 값을 갖는 것입니다.
이러한 값은 인스턴스를 만들 때 결정되며 열거 사례의 각 인스턴스마다 다를 수 있습니다.
열거 케이스 인스턴스의 저장된 속성처럼 동작하는 관련 값을 생각할 수 있습니다.
예를 들어, 서버에 일출과 일몰 시간을 요청하는 경우를 생각해보자. 서버는 요청된 정보로 응답하거나 무엇이 잘못되었는지에 대한 설명으로 응답합니다.

enum ServerResponse {
    case result(String, String)
    case failure(String)
}

let success = ServerResponse.result("6:00 am", "8:09 pm")
let failure = ServerResponse.failure("Out of cheese.")

switch success {
case let .result(sunrise, sunset):
    print("Sunrise is at \(sunrise) and sunset is at \(sunset).")
case let .failure(message):
    print("Failure...  \(message)")
}
// Prints "Sunrise is at 6:00 am and sunset is at 8:09 pm."

구조체는 메서드 및 이니셜라이저를 포함하여 클래스와 동일한 많은 동작을 지원합니다.
구조와 클래스의 가장 중요한 차이점 중 하나는 구조가 코드에서 전달될 때 항상 복사되지만 클래스는 참조로 전달된다는 것입니다.

struct Card {
    var rank: Rank
    var suit: Suit
    func simpleDescription() -> String {
        return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
    }
}
let threeOfSpades = Card(rank: .three, suit: .spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()

Concurrency(동시성)

async를 사용하여 비동기적으로 실행되는 함수를 표시합니다.

func fetchUserID(from server: String) async -> Int {
    if server == "primary" {
        return 97
    }
    return 501
}

await앞에 작성하여 비동기 함수에 대한 호출을 표시 합니다.

func fetchUsername(from server: String) async -> String {
    let userID = await fetchUserID(from: server)
    if userID == 501 {
        return "John Appleseed"
    }
    return "Guest"
}

async let을 사용하여 비동기 함수를 호출하여 다른 비동기 코드와 병렬로 실행되도록 합니다. 반환되는 값을 사용할 때 await를 작성합니다.

func connectUser(to server: String) async {
    async let userID = fetchUserID(from: server)
    async let username = fetchUsername(from: server)
    let greeting = await "Hello \(username), user ID \(userID)"
    print(greeting)
}

Task반환을 기다리지 않고 동기 코드에서 비동기 함수를 호출하는 데 사용 합니다.

Task {
    await connectUser(to: "primary")
}
// Prints "Hello Guest, user ID 97"

Protocols and Extensions(프로토콜 및 확장)

protocol프로토콜을 선언하는 데 사용 합니다.

protocol ExampleProtocol {
    var simpleDescription: String { get }
    mutating func adjust()
}

클래스, 열거형 및 구조는 모두 프로토콜을 채택할 수 있습니다.

class SimpleClass: ExampleProtocol {
    var simpleDescription: String = "A very simple class."
    var anotherProperty: Int = 69105
    func adjust() {
        simpleDescription += "  Now 100% adjusted."
    }
}
var a = SimpleClass()
a.adjust()
let aDescription = a.simpleDescription

struct SimpleStructure: ExampleProtocol {
    var simpleDescription: String = "A simple structure"
    mutating func adjust() {
        simpleDescription += " (adjusted)"
    }
}
var b = SimpleStructure()
b.adjust()
let bDescription = b.simpleDescription

SimpleStructure의 선언 SimpleClass에는 클래스의 메서드가 항상 클래스를 수정할 수 있기 때문에 변형으로 표시된 메서드가 필요하지 않습니다.

extension는 새 메서드 및 계산된 속성과 같은 기존 형식에 기능을 추가하는 데 사용 합니다.

확장을 사용하여 다른 곳에서 선언된 형식 또는 라이브러리나 프레임워크에서 가져온 형식에 프로토콜 적합성을 추가할 수 있습니다.

extension Int: ExampleProtocol {
    var simpleDescription: String {
        return "The number \(self)"
    }
    mutating func adjust() {
        self += 42
    }
}
print(7.simpleDescription)
// Prints "The number 7"

다른 이름으로 된 유형과 마찬가지로 프로토콜 이름을 사용할 수 있습니다.
ex) 유형은 다르지만 모두 단일 프로토콜을 준수하는 개체 컬렉션을 만들 수 있습니다.

프로토콜 유형인 값으로 작업할 때 프로토콜 정의 외부의 메소드는 사용할 수 없습니다.

let protocolValue: ExampleProtocol = a
print(protocolValue.simpleDescription)
// Prints "A very simple class.  Now 100% adjusted."
// print(protocolValue.anotherProperty)  // Uncomment to see the error

변수 protocolValue의 런타임 유형은 SimpleClass이지만 컴파일러는 지정된 ExampleProtocol 유형으로 처리합니다.

즉, 프로토콜 적합성 외에도 클래스가 구현하는 메서드나 속성에 실수로 액세스할 수 없습니다.

Error Handling(오류 처리)

Error프로토콜 을 채택하는 모든 유형을 사용하여 오류를 나타냅니다.

enum PrinterError: Error {
    case outOfPaper
    case noToner
    case onFire
}

throw오류를 발생시키고 throws오류를 발생시킬 수 있는 함수를 표시하는 데 사용합니다.

함수에서 오류가 발생하면 함수가 즉시 반환되고 함수를 호출한 코드가 오류를 처리합니다.

func send(job: Int, toPrinter printerName: String) throws -> String {
    if printerName == "Never Has Toner" {
        throw PrinterError.noToner
    }
    return "Job sent"
}

오류를 처리하는 방법에는 여러 가지가 있습니다.
한 가지 방법은 do-catch를 사용하는 것 입니다.

블록 내부에서 오류를 발생시킬 수 있는 코드를 앞에 do를 작성하여 표시 합니다.

블록 try내에서 다른 이름을 지정하지 않는 한 오류에 자동으로 이름(catcherror)이 지정됩니다.

do {
    let printerResponse = try send(job: 1040, toPrinter: "Bi Sheng")
    print(printerResponse)
} catch {
    print(error)
}
// Prints "Job sent"

catch는 특정 오류를 처리 하는 여러 블록을 제공할 수 있습니다.
스위치에서 catch와 마찬가지로 case패턴을 작성합니다.

do {
    let printerResponse = try send(job: 1440, toPrinter: "Gutenberg")
    print(printerResponse)
} catch PrinterError.onFire {
    print("I'll just put this over here, with the rest of the fire.")
} catch let printerError as PrinterError {
    print("Printer error: \(printerError).")
} catch {
    print(error)
}
// Prints "Job sent"

오류를 처리하는 또 다른 방법(try?)은 결과를 선택 사항으로 변환하는 데 사용합니다.

함수에서 오류가 발생하면 특정 오류는 무시되고 결과는 nil로 반환됩니다.
그렇지 않으면 결과는 함수가 반환한 값을 포함하는 옵셔널값을 포함한 값 입니다.

let printerSuccess = try? send(job: 1884, toPrinter: "Mergenthaler")
let printerFailure = try? send(job: 1885, toPrinter: "Never Has Toner")

defer
함수가 반환되기 직전에 함수의 다른 모든 코드 후에 실행되는 코드 블록을 작성하는 데 사용합니다.

함수에서 오류가 발생하는지 여부에 관계없이 코드가 실행됩니다.

함수가 서로 다른 시간에 실행해야 하는 경우에도 설정 및 정리코드를 나란히 작성 하는 데 사용할 수 있습니다.

var fridgeIsOpen = false
let fridgeContent = ["milk", "eggs", "leftovers"]

func fridgeContains(_ food: String) -> Bool {
    fridgeIsOpen = true
    defer {
        fridgeIsOpen = false
    }

    let result = fridgeContent.contains(food)
    return result
}
fridgeContains("banana")
print(fridgeIsOpen)
// Prints "false"

Generics

일반 함수 또는 유형을 만들려면 꺾쇠 괄호 안에 이름을 씁니다.

func makeArray<Item>(repeating item: Item, numberOfTimes: Int) -> [Item] {
    var result: [Item] = []
    for _ in 0..<numberOfTimes {
        result.append(item)
    }
    return result
}
makeArray(repeating: "knock", numberOfTimes: 4)

일반 형식의 함수 및 메서드는 물론 클래스, 열거형 및 구조를 만들 수 있습니다.

// Reimplement the Swift standard library's optional type
enum OptionalValue<Wrapped> {
    case none
    case some(Wrapped)
}
var possibleInteger: OptionalValue<Int> = .none
possibleInteger = .some(100)

본문 바로 앞에 where를 사용 하여 요구 사항 목록을 지정합니다.
ex) 유형이 프로토콜을 구현하도록 요구하거나, 두 유형이 동일하도록 요구하거나, 클래스가 특정 슈퍼클래스를 갖도록 요구할 때 사용

func anyCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> Bool
    where T.Element: Equatable, T.Element == U.Element
{
    for lhsItem in lhs {
        for rhsItem in rhs {
            if lhsItem == rhsItem {
                return true
            }
        }
    }
    return false
}
anyCommonElements([1, 2, 3], [3])
profile
개발자를 꿈꾸는 학생입니다!

0개의 댓글