스트리밍은 대부분의 브라우저와
Developer 앱에서 사용할 수 있습니다.
-
Swift 제네릭 사용하기
제네릭은 Swift에서 추상 코드를 작성하기 위한 기본 도구입니다. 코드가 진화함에 따라 추상화할 수 있는 기회를 확인하고, 많은 동작이 있는 하나의 코드를 작성하기 위한 전략을 평가하는 방법을 배우고, 제네릭 코드를 더 쉽게 작성하고 이해하는 데 도움이 되는 Swift 5.7의 언어 기능을 알아볼 수 있습니다.
리소스
관련 비디오
WWDC23
WWDC22
-
다운로드
♪ 부드러운 힙합 음악 ♪ ♪ 안녕하세요 Swift 컴파일러 팀의 Holly입니다 ‘Swift 제네릭 수용’에 오신 것을 환영합니다 제네릭은 Swift에서 추상 코드를 작성하는 기본 도구로 코드가 진화할수록 복잡성을 관리하는 데 매우 중요합니다 추상화는 아이디어를 구체적인 세부 사항과 구분합니다 코드에는 추상화가 유용하게 쓰이는 여러 가지 방법이 있죠 여러분이 항상 사용하는 추상화의 한 가지 형태는 코드를 함수나 로컬 변수로 인수분해하는 겁니다 이는 동일한 기능이나 값을 여러 번 사용해야 하는 경우 매우 유용합니다 기능을 함수로 추출하면 세부 정보가 추상화되며 이 추상화를 사용하는 코드는 세부 정보를 반복하지 않고 현재 일어나고 있는 일에 대한 아이디어를 표현할 수 있죠 Swift에서는 구체적인 유형도 추상화할 수도 있습니다 서로 다른 세부 정보를 가진 동일한 아이디어의 유형 집합이 있는 경우 추상 코드를 작성하여 모든 구체적인 유형과 함께 작업할 수 있습니다 오늘은 구체적인 유형을 사용하여 코드를 모델링하고 일련의 구체적인 유형의 공통 기능을 식별하고 이러한 기능을 나타내는 인터페이스를 구축하고 마지막으로 해당 인터페이스를 사용하여 제네릭 코드를 작성하는 워크플로를 살펴보겠습니다 Swift의 추상화 도구에 대해 자세히 알아보고 농장 시뮬레이션을 위한 코드를 구축해 보겠습니다 자, 그럼 구체적인 유형을 작성하는 것으로 시작하죠 ‘소’라는 하나의 구조체로 시작하겠습니다 소에는 건초 유형의 매개 변수를 허용하는 ‘먹기’라는 메서드가 있습니다 건초는 또 다른 구조체죠 이 구조체는 건초를 생산하는 작물인 알팔파를 재배하기 위한 정적 메서드 ‘키우기’를 가지고 있습니다 알팔파 구조체에는 알팔파 인스턴스에서 건초를 수확하는 메서드가 있습니다 마지막으로 소에게 먹이를 주는 메서드를 가진 ‘농장’이라는 구조체를 추가하겠습니다 먹이 주기 메서드는 먼저 건초를 생산하는 알팔파를 재배하고 건초를 수확한 다음 마지막으로 소에게 건초를 먹임으로써 구현할 수 있습니다 이제 저는 농장에서 소에게 먹이를 줄 수 있습니다 하지만 저는 동물 유형을 더 추가하고 싶습니다 말과 닭 같은 다른 동물을 나타내는 구조체를 더 많이 추가할 수 있습니다 저는 농장에서 소, 말, 닭에게 먹이를 주려고 합니다 각 매개 변수 유형을 개별적으로 받아들이도록 먹이 주기 메서드를 오버로드할 수도 있지만 각 오버로드는 매우 비슷한 구현이 존재하겠죠 이 구현은 제가 더 많은 동물을 추가할수록 보일러플레이트가 될 테고 어차피 가장 반복되는 코드일 겁니다 여러분이 반복적인 구현으로 오버로드를 작성하고 있다면 일반화하라는 신호일 수 있습니다 근본적으로 이 구현들은 매우 유사합니다 서로 다른 동물 유형이 기능은 비슷하니까요 다음 단계는 동물 유형 간의 공통적인 기능을 식별하는 것입니다 우리는 모두 특정 유형의 먹이를 먹을 수 있는 동물 유형의 집합을 구축했습니다 각 동물 유형은 먹는 방법이 다를 것이고 따라서 먹기 메서드의 각 구현은 행동에서 차이가 있겠죠 우리는 여기서 추상 코드가 먹기 메서드를 호출하도록 허용하고 추상 코드가 작동하는 구체적인 유형에 따라 다르게 동작하도록 구축하려고 합니다 다양한 구체적인 유형에 대해 추상 코드가 다르게 동작하는 능력을 ‘다형성’이라고 합니다 다형성을 통해 코드 한 개는 사용되는 방식에 따라 여러 가지 동작을 가질 수 있습니다 적절하게도 다형성 자체는 다양한 형태로 나타납니다 첫 번째는 함수 오버로드입니다 여기서 같은 함수 호출은 인수 유형에 따라 다른 의미를 가질 수 있습니다 오버로딩은 실제로 일반적인 해결책이 아니므로 ’Ad Hoc 다형성’이라고 부릅니다 우리는 방금 오버로딩이 얼마나 반복적인 코드로 이어지는지 봤죠 다음은 하위 유형 다형성으로 슈퍼 유형에서 작동하는 코드는 런타임 시 코드가 사용하는 특정 하위 유형에 따라 다른 동작을 가질 수 있습니다 마지막으로 제네릭을 사용하여 달성되는 매개 변수 다형성도 있습니다 제네릭 코드는 유형 매개 변수를 사용하여 서로 다른 유형으로 작동하는 하나의 코드를 작성할 수 있고 구체적인 유형 자체가 인수로 사용됩니다 오버로딩은 이미 배제했으니 하위 유형 다형성을 사용해 보겠습니다 하위 유형 관계를 나타내는 한 가지 방법은 클래스 계층을 사용하는 겁니다 우리는 ‘동물’이라는 클래스를 도입할 수 있죠 다음으로 각 동물 유형을 구조체에서 클래스로 바꿉니다 각 특정 동물 클래스는 동물 슈퍼 클래스로부터 상속되며 먹기 메서드를 재정의합니다 이제 추상적인 기본 클래스 동물이 있습니다 이를 통해 모든 특정 동물 유형을 나타낼 수 있죠 동물 클래스에서 먹기를 호출하는 코드는 하위 유형 다형성을 사용해 하위 클래스 구현을 호출합니다 하지만 아직 안 끝났습니다 동물에서 먹기 메서드에 대한 매개 변수 유형을 아직 입력하지 않았으며 이 코드에는 몇 가지 다른 빨간색 플래그가 있습니다 첫째, 클래스를 사용하면 서로 다른 동물 인스턴스 간에 어떤 상태도 공유할 필요 없거나 공유하기 싫더라도 참조 의미론을 사용하도록 강요합니다 또한 이 전략에는 기본 클래스의 메서드를 재정의하는 하위 클래스가 필요하지만 이 작업을 수행하는 것을 잊어도 런타임까지 탐지되지 않습니다 하지만 이 추상화 모델의 더 큰 문제는 각 동물의 하위 유형이 다른 종류의 음식을 먹는다는 거죠 이 종속성은 클래스 계층으로 표현하기가 정말 어렵습니다 우리가 선택할 수 있는 방법은 메서드가 ‘모든’과 같이 덜 구체적인 유형을 허용하게 하는 겁니다 그러나 이 전략은 런타임에 올바른 유형이 전달됐는지 확인하기 위해 하위 클래스 구현에 의존합니다 그래서 우리는 각각의 재정의된 메서드에 보일러플레이트를 추가로 부과했죠, 더 중요하게도 이를 통해 여러분은 우연히 잘못된 음식 유형을 전달하게 되고 다른 버그가 생기지만 이걸 런타임이 돼서야 발견할 수 있습니다 자, 다른 걸 시도해 보죠 대신 동물 슈퍼 클래스에 유형 매개 변수를 도입하여 동물의 먹이 주기 유형을 유형 안전 방식으로 표현할 수 있습니다 이 형식 매개 변수는 각 하위 클래스에 대한 특정 먹이 유형의 플레이스 홀더 역할을 합니다 이 접근 방식에서는 음식 유형 매개 변수를 동물 클래스의 선언으로 높여야 합니다 조금 부자연스러워 보이죠 동물들이 작동하려면 음식이 필요하긴 하지만 음식을 먹는 것이 동물의 핵심 목적은 아니며 동물들과 함께 작동하는 여러 코드들은 아마도 음식에 대해 전혀 신경 쓰지 않을 테니까요 그렇다고 해도 동물 클래스에 대한 모든 참조는 음식 유형을 지정해야 합니다 예를 들어 각 동물 하위 클래스는 상속 절의 각 꺾쇠괄호 안에 해당 음식 유형을 명시적으로 지정해야 합니다 동물 클래스의 각 사용 장소에 있는 이 보일러플레이트는 각 동물에 고유한 유형을 더 추가하는 경우 부담이 될 수 있습니다 그래서 여기 있는 접근법에는 좋은 인체 공학이나 올바른 의미론이 전혀 없죠 근본적인 문제는 클래스가 데이터 유형이라는 겁니다 또 우리는 슈퍼 클래스가 구체적인 유형에 대한 추상적인 아이디어를 나타내도록 만들기 위해 노력하고 있습니다 대신 우리는 기능의 작동 방식에 대한 세부 정보 없이 유형의 기능을 나타내도록 설계된 언어 구조를 원합니다 동물들에게는 두 가지 공통 기능이 있습니다 각 동물에게는 특정 먹이 유형이 있으며 그 음식 중 일부를 소비하는 작업도 있습니다 우리는 이러한 기능을 나타내는 인터페이스를 구축할 수 있습니다 Swift에서는 프로토콜을 사용하여 이를 수행합니다 프로토콜은 추상화 도구로 적합한 유형의 기능을 설명합니다 프로토콜을 사용하여 유형이 수행하는 작업에 대한 아이디어를 구현 세부 정보와 분리할 수 있습니다 유형이 수행하는 작업에 대한 아이디어는 인터페이스를 통해 표현됩니다 동물의 기능을 프로토콜 인터페이스로 변환해 보겠습니다 프로토콜의 이름은 우리가 설명하는 유형의 범주를 나타냅니다 따라서 이 프로토콜을 ‘동물’이라고 부릅니다 각 기능은 프로토콜 요구 사항에 매핑됩니다 특정 유형의 음식은 동물 프로토콜의 관련 유형에 매핑됩니다 유형 매개 변수와 마찬가지로 관련 유형은 구체적인 유형의 플레이스 홀더 역할을 합니다 관련 유형이 특별한 이유는 프로토콜에 적합한 특정 유형에 따라 모두 다르기 때문입니다 이 관계는 보장되므로 특정 동물 유형의 각 인스턴스는 항상 동일한 먹이 유형을 먹습니다 다음으로 먹이를 소비하는 작업이 메서드에 매핑됩니다 이 메서드는 ‘먹기’라고 하며 동물의 먹이 주기 유형의 매개 변수를 받아들입니다 프로토콜에는 이 메서드의 구현이 없으며 이를 구현하려면 구체적인 동물 유형이 필요합니다 이제 동물 프로토콜을 갖게 되었으니 각 구체적인 동물 유형이 프로토콜에 따르게 할 수 있죠 선언이나 확장 프로그램에서 프로토콜 적합성을 가진 구체적인 유형에 주석을 달 수 있습니다 프로토콜은 클래스에 국한되지 않으므로 구조체, 나열자, 행위자와 함께 프로토콜을 사용할 수도 있습니다 일단 이 적합성 주석을 작성하면 컴파일러는 구체적인 유형이 각 프로토콜의 요구 사항을 구현하는지 확인합니다 각 동물 유형은 반드시 먹기 메서드를 구현해야 하며 먹이 주기 유형은 매개 변수 목록에 사용되므로 컴파일러는 이 유형이 무엇인지 유추할 수 있습니다 또한 유형 별칭을 사용하여 먹이 주기 유형을 명시적으로 작성할 수 있습니다 우리는 성공적으로 동물의 공통 기능을 파악하고 프로토콜 인터페이스를 사용하여 그 기능을 표현했습니다 이제 제네릭 코드를 작성할 수 있습니다 우리는 동물 프로토콜을 사용하여 농장에서 먹이 주기 메서드를 구현할 수 있습니다 우리는 모든 구체적인 동물 유형에 적용되는 하나의 구현을 작성하고자 합니다 매개 변수 다형성을 사용하고 메서드가 호출될 때 구체적인 형식으로 대체될 유형 매개 변수를 도입합니다 유형 매개 변수는 꺾쇠괄호 안의 함수 이름 뒤에 작성됩니다 일반 변수 및 함수 매개 변수와 마찬가지로 유형 매개 변수의 이름도 원하는 대로 지정할 수 있습니다 또한 다른 유형과 마찬가지로 유형 매개 변수의 이름을 사용하여 함수 서명 전체에 걸쳐 유형 매개 변수를 참조할 수 있죠 여기서 저는 ‘A’라는 유형 매개 변수를 선언했고 A를 동물 기능 매개 변수의 유형으로 사용했습니다 우리는 항상 구체적인 동물 유형이 동물 프로토콜에 따르기를 원하니까 프로토콜 적합성으로 유형 매개 변수에 주석을 답니다 프로토콜 적합성은 꺾쇠괄호 안에 작성하거나 이어지는 "where" 절 뒤에 작성할 수 있으며 여기서 서로 다른 유형 매개 변수 간의 관계를 지정할 수도 있습니다 명명된 유형 매개 변수와 이어지는 "where" 절은 정교한 요구 사항 및 유형 관계를 작성할 수 있도록 지원하기 때문에 매우 유용합니다 그러나 대부분의 제네릭 함수에는 이런 일반성이 필요하지 않습니다 먹이 주기 메서드에 초점을 맞춰 봅시다 유형 매개 변수 A는 매개 변수 목록에 한 번 나타나고 "where" 절은 유형 매개 변수에 대한 적합성 요구 사항을 나열합니다 이 경우 유형 매개 변수의 이름을 지정하고 "where" 절을 사용하면 메서드가 실제보다 더 복잡해 보입니다 이 제네릭 패턴은 매우 일반적이어서 더 쉽게 표현할 수 있는 방법이 있습니다 유형 매개 변수를 명시적으로 쓰는 대신 이 추상적인 유형을 프로토콜 적합성의 관점에서 ‘일부 동물’이라고 적음으로써 표현할 수 있습니다 이 선언은 이전 선언과 동일하지만 불필요한 유형 매개 변수 목록입니다 또 "where" 조항은 사라졌죠 그들이 제공하는 표현력이 필요하지 않기 때문입니다 ‘일부 동물’로 작성하는 게 더 간단합니다 구문상의 잡음을 줄여 주고 또 매개 변수 선언에 바로 동물 매개 변수에 대한 의미 정보를 포함하기 때문입니다 동물 구문을 몇 가지 분석해 보겠습니다 ‘일부 동물’의 ‘일부’는 여러분이 현재 작업 중인 특정 유형이 있음을 나타냅니다 '일부' 키워드는 항상 적합성 요구 사항 뒤에 나옵니다 이 경우 특정 유형이 동물 프로토콜을 반드시 준수해야 하며 이를 통해 우리는 매개 변수 값에 동물 프로토콜의 요구 사항을 사용할 수 있습니다 '일부' 키워드는 매개 변수 및 결과 유형에 사용할 수 있습니다 이전에 SwiftUI 코드를 작성한 적 있다면 이미 결과 위치에서 ‘일부 보기’를 사용하여 ‘일부’를 사용했습니다 결과 유형 '일부 보기'는 정확히 동일한 개념입니다 SwiftUI 뷰에서 본문 속성은 특정 유형의 보기를 반환하지만 본문 속성을 사용하는 코드는 특정 유형이 무엇인지 알 필요가 없습니다 특정 추상적 유형의 개념을 더 잘 이해하기 위해 한 걸음 뒤로 물러나 보겠습니다 특정 구체적인 유형의 플레이스 홀더를 나타내는 추상 유형을 불투명 유형이라고 합니다 대체되는 특정 구체적인 유형을 기본 유형이라고 합니다 불투명 유형이 있는 값의 경우 값의 범위에 대해 기본 유형이 고정됩니다 이렇게 하면 값을 사용하는 제네릭 코드는 값에 액세스할 때마다 동일한 기본 유형을 얻을 수 있습니다 ‘일부' 키워드와 꺾쇠괄호 안의 명명된 유형 매개 변수를 사용하는 유형은 모두 불투명 유형을 선언합니다 불투명 유형은 입력과 출력 모두에 사용할 수 있으므로 매개 변수 위치 또는 결과 위치에서 선언할 수 있습니다 함수 화살표는 이러한 위치 사이의 구분선입니다 불투명 유형의 위치는 프로그램의 어떤 부분이 추상 유형을 보고 프로그램의 어떤 부분이 구체적인 유형을 결정하는지 결정합니다 명명된 유형 매개 변수는 항상 입력 측에서 선언되므로 호출자는 기본 유형을 결정하고 구현은 추상 유형을 사용합니다 일반적으로 불투명 매개 변수 또는 결과 유형에 대한 값을 제공하는 프로그램의 부분이 기본 유형을 결정하고 이 값을 사용하는 프로그램의 부분은 추상 유형을 봅니다 매개 변수 및 결과 값에 대한 직관에 따라 이 기능이 어떻게 작동하는지 살펴보겠습니다 기본 유형은 값에서 추론되므로 기본 유형은 항상 값과 동일한 위치에서 나옵니다 로컬 변수의 경우 기본 유형은 할당 오른쪽에 있는 값에서 추론됩니다 즉, 불투명 유형의 로컬 변수는 항상 초기 값을 가져야 합니다 이를 제공하지 않으면 컴파일러에서 오류를 보고합니다 변수 범위에 대해 기본 유형이 고정되어야 하므로 기본 유형을 변경하려고 해도 오류가 발생합니다 불투명 유형이 있는 매개 변수의 경우 기본 유형은 호출 사이트의 인수 값에서 추론됩니다 매개 변수 위치에 사용하는 '일부'는 Swift 5.7의 새 기능이죠 기본 유형은 매개 변수의 범위에 대해서만 고정되면 됩니다 따라서 각 호출은 서로 다른 인수 유형을 제공할 수 있죠 불투명 결과 유형의 경우 기본 유형은 구현의 반환 값에서 추론됩니다 불투명 결과 유형을 가진 메서드 또는 계산된 속성은 프로그램의 모든 위치에서 호출할 수 있으므로 이 명명된 값의 범위는 전역적입니다 즉, 기본 반환 유형은 모든 반환 구문에서 동일해야 합니다 그렇지 않으면 컴파일러는 기본 반환 값의 유형이 일치하지 않는다는 오류를 보고합니다 불투명 SwiftUI 뷰의 경우 ViewBuilder DSL은 각 분기에 대해 동일한 기본 반환 유형을 갖도록 제어 흐름 구문을 변환할 수 있습니다 따라서 이 경우 View Builder DSL을 사용하여 문제를 해결할 수 있습니다 @ViewBuilder 주석을 메서드에 작성하고 반환 구문을 제거하면 ViewBuilder 유형별로 결과를 빌드할 수 있습니다 feedAnimal 메서드로 다시 돌아가겠습니다 불투명 유형을 다른 곳에서 참조할 필요 없으므로 매개 변수 목록에서 '일부'를 사용할 수 있습니다 함수 서명에서 불투명 유형을 여러 번 참조해야 하는 경우 이름 유형 매개 변수가 유용하게 사용됩니다 예를 들어 우리가 동물 프로토콜에 ‘서식지’라는 또 다른 관련 유형을 추가하면 우리는 어떤 동물을 위해 농장에 서식지를 만들 수 있기를 원할 수도 있죠 이 경우 특정 동물 유형에 따라 결과 유형이 달라지기 때문에 매개 변수 유형과 반환 유형에 유형 매개 변수 A를 사용해야 합니다 불투명 유형을 여러 번 참조해야 하는 또 다른 일반적인 위치는 제네릭 유형입니다 코드는 종종 제네릭 유형에 유형 매개 변수를 선언하고 저장된 속성에 유형 매개 변수를 사용하며 멤버별 이니셜라이저에서 다시 사용합니다 다른 맥락에서 제네릭 유형을 참조하려면 꺾쇠괄호 안에 유형 매개 변수를 명시적으로 지정해야 합니다 선언의 꺾쇠괄호는 제네릭 유형을 사용하는 방법을 명확히 하는 데 도움이 되므로 불투명 유형은 제네릭 유형에 대해 항상 이름을 지정해야 합니다 이제 먹이 주기 메서드의 구현을 구축해 보겠습니다 동물 매개 변수의 유형을 사용하여 먹이 주기 관련 유형을 통해 재배할 작물 유형에 액세스할 수 있습니다 Feedgrow()를 호출하여 이 먹이 주기 유형을 생성하는 작물의 인스턴스를 확인합니다 다음으로 작물로부터 농작물을 수확해야 하는데 ‘수확’이라는 작물 유형이 제공하는 메서드를 호출하여 수행할 수 있습니다 마지막으로 이 농작물을 동물에게 먹일 수 있습니다 기본 동물 유형이 고정되어 있기 때문에 컴파일러는 다양한 메서드 호출에 걸쳐 식물 유형과 생산물 유형 동물 유형 간의 관계를 알고 있습니다 이러한 정적 관계는 우리가 동물에게 잘못된 종류의 음식을 먹이는 실수를 저지르지 못하게 막죠 우리가 이 동물에 대해 정확한 먹이 유형으로 보장되지 않는 유형을 사용하려고 한다면 컴파일러는 알려 줍니다 다른 농장 프로토콜들이 동물 먹이 유형과 식물 사이의 관계를 표현하기 위해 어떻게 만들어졌는지 알고 싶다면 ’Swift 프로토콜 인터페이스 설계’를 참조하세요 마지막으로 모든 동물에게 먹이를 주는 메서드를 추가해 보겠습니다 배열을 받아들이는 feedAll 메서드를 추가해 보죠 요소 유형이 동물 프로토콜을 따라야 한다는 것은 알지만 저는 배열이 다른 동물 유형을 저장하고 싶습니다 일부 동물이 우리를 도와줄 수 있을지 봅시다 '일부'에는 변경할 수 없는 특정 기본 유형이 있습니다 기본 유형은 고정되므로 배열의 모든 요소가 동일한 유형을 가져야 합니다 그래서 일부 동물의 배열은 올바로 표현하지 못합니다 저는 다른 동물 유형을 수용할 배열이 필요하기 때문입니다 여기서 우리는 모든 동물 유형을 대표할 수 있는 슈퍼 유형이 반드시 필요합니다 우리는 '모든 동물'이라고 적어 임의의 동물 유형을 표현할 수 있습니다 ‘모든' 키워드는 이 유형이 임의의 동물 유형을 저장할 수 있으며 기본 동물 유형은 런타임에 달라질 수 있음을 나타냅니다 '일부' 키워드와 마찬가지로 '모든' 키워드는 항상 적합성 요구 사항 뒤에 나옵니다 모든 동물은 모든 구체적인 동물 유형을 동적으로 저장할 수 있는 단일 정적 유형으로 값 유형을 가진 하위 유형 다형성을 사용할 수 있습니다 이러한 유연한 스토리지를 허용하기 위해 모든 동물 유형은 메모리에 특별한 표현을 가집니다 이 표현을 상자처럼 생각할 수 있습니다 값이 상자 안에 직접 들어갈 정도로 작은 경우도 가끔 있습니다 또 어떤 값은 상자에 넣기에 너무 커서 값을 다른 곳에 할당해야 하고 상자에는 해당 값에 대한 포인터가 저장됩니다 모든 구체적인 동물 유형을 동적으로 저장할 수 있는 정적 유형 모든 동물을 공식적으로 실존 유형으로 부릅니다 다른 구체적인 유형에 대해 동일한 표현을 사용하는 전략을 '유형 소거'라고 합니다 구체적인 유형은 컴파일 시 소거된다고 하며 구체적인 유형은 런타임 시에만 알려집니다 실존 유형 모든 동물의 이 두 인스턴스는 정적 유형이 같지만 동적 유형이 다릅니다 유형 소거는 서로 다른 동물 값 사이의 유형 수준 구분을 제거하며 이를 통해 서로 다른 동적 유형을 가진 값을 동일한 정적 유형으로 상호 교환하여 사용할 수 있습니다 유형 소거를 사용하여 feedAll 메서드에서 원하는 값 유형의 이기종 배열을 작성할 수 있습니다 우리는 모든 동물의 배열을 매개 변수 유형으로 사용합니다 관련 유형이 있는 프로토콜에 '모든' 키워드를 사용하는 건 Swift 5.7의 새로운 기능입니다 feedAll 메서드를 구현하기 위해 먼저 동물의 배열을 반복하겠습니다 동물 프로토콜에서 각 동물에 대해 먹기 메서드를 호출하려고 합니다 이 방법을 호출하려면 이 반복에서 기본 동물을 위한 특정 먹이 주기 유형을 얻을 필요가 있습니다 하지만 모든 동물에서 먹기를 호출하려고 하면 컴파일러 오류가 발생합니다 특정 동물 유형 간의 유형 수준의 구분을 제거했기 때문에 우리는 특정 동물 유형에 따라서 달라지는 모든 유형의 관계도 제거했습니다 관련 유형을 포함해서요 그래서 이 동물이 어떤 유형의 사료를 기대하는지 알 수 없습니다 유형 관계에 의존하려면 특정 유형의 동물이 고정된 맥락으로 돌아갈 필요가 있습니다 모든 동물에서 직접 먹기를 호출하는 대신 일부 동물을 수락하는 먹이 주기 메서드를 호출해야 합니다 모든 동물은 일부 동물과 다른 유형이지만 컴파일러는 기본값을 언박싱하고 일부 동물 매개 변수에 이를 직접 전달하여 모든 동물의 인스턴스를 일부 동물로 변환할 수 있습니다 이러한 언방싱 인수 기능은 Swift 5.7의 새 기능입니다 여러분은 언박싱이 컴파일러가 상자를 열고 내부에 저장된 값을 꺼내는 행위라고 생각할 수 있습니다 일부 동물 매개 변수 범위의 경우 값은 고정된 기본 유형을 가지므로 관련 유형에 대한 액세스를 포함하여 기본 유형에 대한 모든 작업에 액세스할 수 있습니다 이 기능은 우리가 필요할 때 유연한 스토리지를 선택할 수 있도록 허용하는 동시에 함수 범위에 대한 기본 유형을 수정하여 정적 유형 시스템의 완전한 표현성을 제공하는 맥락으로 돌아갈 수 있게 하므로 매우 유용합니다 또 대부분의 경우 언박싱에 대해 생각할 필요가 없습니다 언박싱은 여러분이 예상하는 대로 작동하니까요 모든 동물에서 프로토콜 메서드를 호출하는 것은 실제로 기본 유형에서 메소드를 호출하는 것과 비슷합니다 따라서 먹이 주기 메서드로 각 동물을 전달할 수 있으며 이 메서드에서는 각 반복에서 특정 동물에게 먹일 적절한 작물을 재배하고 수확할 수 있습니다 이 과정을 통해 '일부'와 '모든’이 서로 다른 기능을 지녔다는 것을 확인했죠 ‘일부’를 사용하면 기본 유형이 고정됩니다 이를 통해 제네릭 코드의 기본 유형에 대한 유형 관계에 의존할 수 있습니다 따라서 작업 중인 프로토콜의 API 및 관련 유형에 대한 전체 액세스를 갖게 됩니다 임의의 구체적인 유형을 저장할 경우는 '모든'을 사용하죠 '모든'은 유형 소거를 제공합니다 이를 통해 이기종 컬렉션을 나타내고 기본 유형의 부재를 나타내며 옵션을 사용하고 추상화를 구현 세부 정보로 만들죠 일반적으로 기본값으로 '일부'를 작성하고 임의 값을 저장해야 할 경우 '일부'를 '모든'으로 변경합니다 이 접근법을 사용하면 여러분에게 스토리지 유연성이 필요한 경우에만 유형 소거 비용과 의미 제한에 대한 비용을 지불하면 됩니다 이 워크플로는 변환이 필요하다는 걸 알 때까지 기본값으로 let-constant를 작성하는 것과 유사합니다 이 세션에서는 코드가 진화하고 더 많은 기능을 획득함에 따라 코드를 일반화하는 워크플로를 살펴봤습니다 구체적인 유형의 작성부터 시작했죠 코드가 더 많은 기능을 획득함에 따라 서로 다른 구체적인 유형 간에 반복이 있음을 눈치챘고 거기서 공통 기능을 식별한 다음 프로토콜을 사용해서 일반화했습니다 마지막으로 ‘일부’와 ‘모든’을 사용하여 추상 코드를 작성했고 좀 더 표현적인 코드에서는 ‘일부’를 선호한다는 걸 논의했죠 프로토콜 작성 및 유형 소거에 대해 자세히 알아보려면 ’Swift 프로토콜 인터페이스 설계’를 참조하세요 함께해 주셔서 감사합니다 즐거운 WWDC 되세요 ♪
-
-
27:10 - Complete example
protocol AnimalFeed { associatedtype CropType: Crop where CropType.Feed == Self static func grow() -> CropType } protocol Crop { associatedtype Feed: AnimalFeed where Feed.CropType == Self func harvest() -> Feed } protocol Animal { associatedtype Feed: AnimalFeed func eat(_ food: Feed) } struct Farm { func feed(_ animal: some Animal) { let crop = type(of: animal).Feed.grow() let produce = crop.harvest() animal.eat(produce) } func feedAll(_ animals: [any Animal]) { for animal in animals { feed(animal) } } } struct Cow: Animal { func eat(_ food: Hay) {} } struct Hay: AnimalFeed { static func grow() -> Alfalfa { Alfalfa() } } struct Alfalfa: Crop { func harvest() -> Hay { Hay() } }
-
-
찾고 계신 콘텐츠가 있나요? 위에 주제를 입력하고 원하는 내용을 바로 검색해 보세요.
쿼리를 제출하는 중에 오류가 발생했습니다. 인터넷 연결을 확인하고 다시 시도해 주세요.