들어가며
워크플로우를 사용자 정의할 수 있는 새로운 tooling과 몇 가지 놀라운 내부 개선 사항에 대해 알아봅니다.
•
최신 동시성 모델
•
전체 스레드 안정성(full-thread safety)
•
몇 가지 언어 개선 사항(더 깔끔하고 간단한 제네릭 및 강력한 새 문자열 처리 기능)
Community Update
+) ~ 멘토쉽 프로그램 얘기
리눅스 패키지 포맷에 대한 지원을 추가했습니다.
•
리눅스 플랫폼을 위한 Swift toolchain distribution process를 간소화했습니다.
•
새로운 toolchain installers를 사용해서 Amazon Linux 2, CentOS 7용 RPM을 다운로드할 수 있습니다.
RPM
Swift의 비전은 항상 높은 수준의 스크립트에서 bare-metal 환경에 이르기까지 모든 것을 사용할 수 있도록 확장성을 갖추는 것이었습니다. 따라서 Swift가 이전에 사용되지 않았던 곳에서 사용되도록 장려하기 위해, Swift는 올해 몇 가지 중요한 변화를 겪었습니다.
•
독립형, 정적으로 링크된 바이너리를 위해 표준 라이브러리를 더 작게 만들려고 우리는 외부 유니코드 지원 라이브러리에 대한 의존성을 없애고 더 빠른 네이티브 구현으로 대체했습니다.
•
더 작고 빠른 바이너리는 이벤트 기반 서버 솔루션에서 실행할 때 큰 이점입니다.
•
기본적으로 리눅스에서 정적 링크를 사용하면 서버에 대한 컨테이너형 배포를 더 잘 지원할 수 있습니다.
•
Swift가 제한된 환경에도 적합하게 만들어 애플의 Secure Enclave Processor에서 사용할 수 있게 해줍니다.
bare-metal 환경
Secure Enclave Processor
Swift Packages
Swift는 앱에서 서버, 제한된 프로세서에 이르기까지 매우 유용합니다. 이 모든 것을 하나로 묶는 것이 패키지 생태계입니다.
•
올해 스위프트 패키지의 새로운 기능이 여러분의 삶을 더 좋게 만들 겁니다.
•
Swift Package Manager가 TOFU를 도입했습니다.
Trust On First Use, 패키지가 처음 다운로드될 때 패키지의 지문이 기록되는 새로운 보안 프로토콜
•
이후 다운로드에서는 이 지문의 유효성을 검사하고 지문이 다른 경우 오류를 보고합니다.
: 이는 신뢰와 보안이 패키지 생태계의 핵심에 어떻게 구축되어 있는지 보여주는 한 가지 예에 불과합니다.
Command plugins
Swift 개발자의 워크플로우를 개선할 수 있는 좋은 방법
•
확장성과 보안성이 뛰어난 빌드 도구를 제공하기 위한 첫 번째 단계
•
문서 생성, 소스 코드 재포맷 등에 사용 가능
•
셸 스크립트로 자동화를 작성하고 별도의 워크플로우를 유지 관리하는 대신 Swift 사용 가능
◦
Open source formatters
◦
Linters
Xcode와 Swift Package Manager에서 모든 오픈 소스 툴을 사용할 수 있습니다.
: Command plugins은 오픈 소스 툴과 Swift Package Manager 간의 접착제 역할을 합니다.
*Swift 프로젝트는 오픈 소스 커뮤니티에 개발자 툴을 수용하여 자동화된 워크플로우와의 원활한 통합을 제공합니다.
docC는 문서를 소스 코드에 통합하기 위한 훌륭한 도구
*Objective-C와 C의 지원으로 더 나아졌다.
: 플러그인은 단순한 스위프트 코드입니다.
1. 명령 플러그인 프로토콜을 준수하는 Struct를 생성하여 플러그인을 정의할 수 있습니다.(MyPlugin) |
2. 플러그인에 호출할 도구를 알려주는 기능을 추가합니다.(performCommand) |
3. 함수 내에서 우리는 DocC를 호출하고 싶습니다. 플러그인을 정의하면 Swift PM 명령줄 인터페이스와 Xcode를 통해 메뉴 항목으로 사용할 수 있습니다. |
4. 우리는 Swift PM에게 문서를 생성하라고 말할 수 있고, 이 작업을 docC 실행 파일로 전달할 수 있습니다. |
Build tool plugins
빌드하는 동안 추가 단계를 주입할 수 있는 패키지
•
Build tool plugins을 구현하면 샌드박스에서 빌드 시스템을 실행할 명령이 생성됩니다.
•
Command plugins과 다르며 패키지의 파일을 변경할 수 있는 명시적 권한을 부여
•
소스 코드 생성 또는 Special 타입의 파일에 대한 사용자 지정 처리에 사용
Build tool plugins Package Layout
: 이 예에서는 패키지 플러그인 target를 구현하는 plugin.swift 스크립트가 사용됩니다.
*플러그인은 Swift 실행 파일로 처리됩니다.
Swift 실행 파일을 작성하는 것과 동일한 방식으로 플러그인을 작성합니다.
실행할 명령과 그 결과로 예상되는 출력을 빌드 시스템에 알려주는 빌드 명령 집합을 정의하여 플러그인을 구현할 수 있습니다.
•
패키지 플러그인은 패키지의 확장성을 제공하는 보안 솔루션
: 패키지 사용을 확장할 때 모듈 충돌이 발생할 수 있습니다.
When?
두 개의 개별 패키지가 동일한 이름의 모듈을 정의하는 경우
이러한 상황을 해결하기 위해 Swift 5.7은 모듈 명확화(disambiguation)를 도입합니다.
•
모듈을 정의하는 패키지 외부에서 모듈의 이름을 변경할 수 있는 기능
example) 로깅 모듈을 정의하는 두 개의 패키지가 서로 충돌
: 이 문제를 해결하기 위해서는 패키지 목록의 종속성 섹션에 moduleAliases keyword 모듈을 추가하기만 하면 됩니다.
이렇게 하면 두 개의 서로 다른 이름을 사용하여 이전에 동일한 이름을 가진 모듈을 구분할 수 있습니다.
Performance improvements
Swift 5.7은 몇 가지 놀라운 성능 향상을 제공합니다.
: 먼저 빌드 시간을 살펴보겠습니다!
: 작년에 우리는 스위프트 드라이버를 어떻게 다시 만들었는지 말씀드렸습니다.
*스위프트 드라이버 : 스위프트 소스 코드를 컴파일하는 프로그램
: 작년의 재구축은 빌드 속도를 크게 높이는 몇 가지 중요한 변경 사항을 제시했습니다.
이제 별도의 실행 파일 대신 Xcode 빌드 시스템 내에서 직접 프레임워크로 사용 가능합니다!
•
빌드 시스템과 밀접해서 빌드들을 조정하여 병렬화와 같은 것들을 가능하게 해줍니다.
: 빌드 속도가 얼마나 빠른지 보여드리기 위해 Swift에서 자주 사용하는 몇 가지 툴을 빌드하는 데 걸리는 시간을 살펴보겠습니다.
•
10코어 iMac에서는 5%에서 25%까지 향상되었습니다.
•
type checking의 속도가 향상되었습니다.
: 우리는 제네릭 시스템의 핵심 부분인 프로토콜과 “Where”절과 같은 함수 signature을 계산하는 부분을 다시 구현하여 Type-checker 성능을 향상시켰습니다.
•
이전에는 더 많은 프로토콜이 포함됨에 따라 시간과 메모리 사용량이 기하급수적으로 증가할 수 있었습니다.
example) 좌표계(Coordinate System)를 정의하는 복잡한 프로토콜 세트
: 많은 associated types에 대한 많은 Generic requirement를 가지고 있어요.
: 이전에는 이 코드를 입력하는 데 17초가 걸렸습니다.
: 하지만 이제 Swift 5.7에서는 이 예제가 훨씬 더 빠르게 1초 안에 type-check 가능합니다.
: 우리는 또한 런타임 개선을 진행했습니다.
Swift 5.7 이전에는 iOS에서 앱을 시작 시 프로토콜 확인에 4초정도의 시간이 걸리는 것을 봤습니다. 앱을 시작할 때마다 프로토콜을 계산해야 했기 때문에 프로토콜을 추가할수록 실행 시간이 길어졌습니다.
: 이제, 캐시되었습니다.
앱 작성 방법과 사용한 프로토콜 수에 따라 iOS 16에서 실행할 때 일부 앱에서 실행 시간이 절반으로 단축될 수 있습니다.
Concurrency updates
올해, 우리는 data race 안전을 최우선으로 하여 모델을 더욱 구체화했습니다.
: Concurrency가 앱의 코드베이스에 대한 매우 근본적이고 중요한 개선 사항이었기 때문에, 우리는 iOS 13과 macOS Catalina까지 이러한 변경 사항을 역배포했습니다.
*이전 운영 체제에 배포하기 위해 앱은 이전 운영체제용 Swift 5.5 concurrency runtime 복사본을 넣습니다.
: 모델을 새로운 방향으로 가져왔습니다. language feature 및 supporting packages를 도입했습니다.
Data race avoidance
: Swift의 정말 중요한 기능 중 하나는 기본적으로 메모리 안전입니다. Swift 사용자는 값을 수정하는 동안 값을 읽는 것과 같이 예측할 수 없는 동작으로 작업을 수행할 수 없습니다.
example) 배열의 개수와 일치하는 배열의 숫자를 모두 제거
1. 처음에는 Array의 count가 3이므로 Array에서 3를 제거합니다. 다 하고 나면 2가 되겠죠? |
2. 그 다음에는 3과 2를 배열에서 제거해야할까요? 아니면 3만 제거해야하나요? |
정답은 둘 다 아니다.
Swift를 사용하면 배열 Count를 수정하는동안 배열 Count에 접근하는 것이 안전하지 않으므로 이 작업을 수행할 수 없습니다.
: 우리의 목표는 thread safety 비슷한 것을 달성하는 것입니다. 우리는 기본적으로 low-level data race를 제거하는 언어를 구상하는 겁니다.
즉, 예측 불가능한 동작을 유발할 수 있는 동시성 버그를 방지하는 겁니다.
example) 아까와 동일한 배열에 0을 추가하는 백그라운드 작업을 생성한 다음 배열의 마지막 요소 제거
: 마지막 요소를 제거하는 것은 0을 더하기 전인가요? 아니면 후인가요?
정답은 둘 다 아니다.
Swift 백그라운드 작업에서 배열을 수정하는 것이 안전하지 않기 때문에 Actor와 같은 동기화 접근을 하지 않고 배열을 수정하는 것은 안전하지 않기 때문에 이 작업을 수행하지 못하도록 차단합니다.
Actor는 data race를 없애기 위한 첫 번째 주요 단계입니다.
: 여러분은 각 Actor들을 동시성의 바다에 있는 다른 모든 것들로부터 격리된 자신만의 섬이라고 생각할 수 있습니다.
: 그러나 서로 다른 스레드에서 각 Actor가 가진 저장된 쿼리 정보를 원할 때는 어떻게 될까요?
: "Eliminate data races using Swift Concurrency.” Session를 보세요.
메모리 안전에서 스레드 안전까지 도달하는 게 Swift 6의 목표
: 우리가 그곳에 도달할 수 있도록 우리는 먼저 새로운 language feature로 작년 동시성 모델을 개선했습니다. 두 번째는 잠재적 data race를 식별하는 새로운 opt-in 안전성 검사입니다.
Build Setting에서 더 엄격한 동시성 검사를 활성화해서 실험해볼 수 있습니다!
: Actor를 다시 살펴볼게요.
우리는 actor isolation 개념을 받아들일 수 있고, 배포된 actor는 더 멀리 갈 수 있습니다. 배포된 actor들은 그들 사이에 네트워크와 함께 다른 machine들을 놓습니다.
•
이 새로운 language feature는 distributed 시스템을 훨씬 더 쉽게 개발할 수 있게 해줍니다.
example) 게임 앱을 만들고 싶다고 가정해 봅시다.
이제 Swift에서 백엔드를 쉽게 작성할 수 있습니다. 여기서 배포된 Actor는 다른 Actor와 비슷하지만 다른 컴퓨터에 있을 수 있습니다.
: 사용자와 게임을 하는 동안 상태를 유지하는 컴퓨터 플레이어를 살펴볼게요.
•
distributed 키워드는 원격 컴퓨터에 있을 수 있는 Actor를 호출해야 하는 함수에 추가할 수 있습니다.
: endOfRound 라는 함수를 추가해 봅시다. 플레이어들을 loop하고 각 플레이어에 대한 makeMove를 호출합니다.
•
이 선수들 중 일부는 로컬이거나 원격일 수도 있지만, “우리는 어느 것이 어느 것일까?”하고 신경 쓸 필요가 없습니다.
일반적인 Actor 호출과 다른 점은 distributed actor 호출이 네트워크 오류로 인해 실패할 수 있다는 것
•
네트워크 장애가 발생하면 actor 메서드가 오류를 발생시킵니다.
•
Actor 밖에서 함수를 호출할 때 필요한 일반적인 wait 키워드와 함께 try 키워드를 추가해야 합니다.
: 이러한 핵심 언어 기본 요소를 기반으로 Swift에서 서버 측 클러스터된 분산 시스템을 구축하는 데 초점을 맞춘 오픈 소스 Distributed Actors package를 구축했습니다.
Package에는 SwiftNIO를 사용하는 통합 네트워크 계층이 포함되어 있으며 SWIM consensus 프로토콜을 구현하여 클러스터 전체의 상태를 관리합니다.
: Swift 5.5와 함께 출시된 Async Sequence를 처리할 때 일반적인 작업에 쉽게 즉시 사용할 수 있는 솔루션을 제공하기 위해 새로운 오픈 소스 Algorithm 세트를 출시했습니다.
•
이러한 API를 패키지로 릴리즈하면 여러 플랫폼 및 운영 체제 버전을 유연하게 배포할 수 있습니다.
: 여러 비동기 시퀀스를 결합하고 값을 집합으로 그룹화하는 방법에는 여러 가지가 있습니다. 이것들은 패키지에 포함된 알고리즘의 일부일 뿐입니다.
: 동시성의 또 다른 측면은 성능입니다.
올해부터는 Actor의 우선 순위를 정해 Actor의 우선순위가 가장 높은 일부터 먼저 실행합니다.
•
운영 체제 스케줄러와 긴밀한 통합을 계속함으로써, 모델은 우선 순위 반전 방지 기능이 내장되어 중요도가 낮은 작업은 우선 순위가 높은 작업을 차단할 수 없습니다.
: 당신의 앱에서 동시성이 성능에 미치는 영향을 시각화하는 것은 정말 어려웠어요. 하지만 지금 우리는 그것을 정확하게 보여주는 새로운 툴을 가지고 있습니다.!
Instruments에 있는 새로운 Swift Concurrency 뷰를 통해 성능 문제를 조사할 수 있습니다.
•
Swift Tasks 및 Swift Actors Instruments는 동시성 코드를 시각화하고 최적화하는 데 도움이 되는 전체 도구 모음을 제공합니다.
•
윗 부분에서 Swift Tasks Instrument는 동시에 실행되는 Task의 수, 해당 시점까지 생성된 총 Task를 포함하여 유용한 통계를 제공합니다.
•
창 아래쪽 Task Forest에서는 구조화된 concurrency 코드 간의 부모-자녀 관계를 그래픽으로 표현합니다. Swift Actor Instrument에 대한 상세 뷰 중 하나에 불과해요!
Expressive Swift
스위프트가 무언가를 표현하기 위한 좋은 도구를 가지고 있다면, 사람들은 그것을 더 자주 사용할 것입니다. 그리고 올해, 스위프트 도구들은 많은 면에서 향상되었습니다. 그 변화 중 일부는 여러분들이 자주 하는 일을 간단하고 편리하게 만들었습니다.
example) 양쪽에 같은 이름을 가진 if let이 있는 경우, 이렇게 쓰는 것이 일반적
: 만약 이름이 정말 길면 반복이 거추장스러워질거예요. 줄여서 말하고 싶겠지만 약간 cryptic해지겠죠..? 약어를 쓰기도 애매하구요..?
Swift 5.7은 패턴에 대한 새로운 단축형을 도입했습니다.
: guard let에서도 사용 가능 합니다!
example) Swift는 항상 closure 안에 쓰여진 코드를 기반으로 어떤 유형의 타입이 반환되는지 파악
compactMap 호출에서 closure는 parseLine의 값을 반환하고 parseLine 함수는 MailmapEntry를 반환하므로 Swift는 entry가 MailmapEntry의 배열이어야 함을 알 수 있습니다.
: 여러 개의 명령문 또는 제어 흐름 기능을 가진 더 복잡한 closure에 대해 작동시켜봅니다.
closure return 유형을 수동으로 지정할 필요 없이 Do-catch 또는 if…else를 사용하거나 print call를 추가할 수 있습니다.
: 우리가 다음에 살펴볼 것은 실제 위험을 나타내지 않는 위험 깃발입니다.
Swift는 타입과 메모리 안전에 매우 관심이 많습니다. 실수를 방지하기 위해 포인터 유형이 다른 포인터 간 또는 원시 포인터와 입력된 포인터 간을 자동으로 변환하지 않습니다.
특정 변환을 허용하는 C와는 매우 달라요.
포인트의 signed-ness를 변경하거나, char star에 포인터를 캐스팅하여 byte로 액세스할 수 있습니다.
: 포인터 동작의 이러한 차이는 C API를 Swift로 가져올 때 문제를 일으킬 수 있어요.
C의 자동 변환에 의해 처리되지만 Swift에서는 오류가 발생할 수 있어요. Swift에서는 한 유형의 포인터가 다른 유형인 것처럼 액세스하는 것은 매우 위험하므로 사용자가 수행하는 작업을 매우 명시적으로 설명해야 합니다.
우리가 포인터를 C에게 직접 넘긴다면? 모두 무의미
Why?
C에서 포인터 불일치가 완벽하게 합법적이기 때문이다. 매우 간단한 것을 위험하게 다뤘다.
: Swift가 타입의 안전을 중시하는 만큼 C-family 코드에 쉽게 접근할 수 있는 것도 중시해줘야 합니다.
: Swift project가 C++ working group를 형성하여 동등하게 기능하는 C++ interop을 구축하기 시작한 이유입니다. 우리는 C 기능을 사용하는 것이 불필요하게 고통스러운 것을 원하지 않아요.
Swift는 가져온 함수와 메서드에 대한 호출에 대해 별도의 규칙을 가지고 있습니다. 일반적으로 스위프트에 있지 않더라도 C에서 합법적일 수 있는 포인터 변환을 허용합니다.
: 이렇게 하면 Swift 코드가 이런 API에서 원활하게 사용될 수 있습니다!
String Processing
올해 Swift는 문자열에서 정보를 추출하는 새로운 도구도 개발했습니다.
: 문자열에서 일부 정보를 구문 분석하는 기능이 있습니다.
: 문자열 인덱스를 조작하는 것이 얼마나 우스꽝스러울 수 있어요. 하지만 저는 좀 더 큰 그림을 놓치고 있다는 생각이 듭니다. 왜냐하면 우리가 구문을 바꿨다고 해도, 여러분이 이 코드를 볼 때 묻는 기본적인 질문에 대답하는 데 도움이 되지 않기 때문입니다.
: 이 코드에 전달된 line variable은 실제로 어떻게 생겼을까요? String를 분해하려고 한걸까요?
: 당신이 충분히 오랫동안 코드를 쳐다본다면 메일 맵의 단순화된 버전을 구문 분석하고 있다는 것을 깨닫게 될겁니다.
: 당신이 오래된 커밋에서 개발자의 이름을 수정하기 위해 Git 저장소에 넣는 파일이에요.
: 하지만 searching과 slicing으로 그 정보를 추출해내는 건 너무 어려워요..
: 우리는 String를 추출해내는 것에 빠져서 무엇이 문제인지 모르게됩니다. 문제는 이 전체입니다.
: 우리는 다른 접근법이 필요해요!
: 우리가 찾고자하는 문자열에 대한 그림을 그리고 언어로 그렇게 표현하는 접근법을 진행합니다.
Swift 5.7에서는 이제 Regex를 작성함으로써 이를 수행할 수 있습니다.
•
Regex는 문자열에서 패턴을 설명하는 방법입니다.
•
50년 이상동안 언어와 툴들은 개발자들이 밀집된 정보로 Regex를 작성할 수 있게 해주었습니다.
Regex는 이제 Swift의 Regex 리터럴에 의해 지원되며 다른 개발자 도구에서처럼 작동합니다.
: 여러분 중 일부는 정규식을 사용해본 적이 없어서 아마 "그게 진짜 코드인가요, 아니면 고양이가 키보드를 가로질러 걸어갔나요?"라고 생각할 것입니다.
: “I don’t blame you”
•
Regex 표현 리터럴은 기호와 mnemonics로 쓰여져 있는데, 그것들을 읽기 위해서는 외워야 합니다.
: Regex 리터럴은 너무 의미를 압축해서 경험이 많은 개발자들도 복잡한 것을 이해하는데 1분이 필요할 때가 있어요.
: 하지만 여러분이 같은 종류의 규칙을 기호 대신 단어로 쓸 수 있다면 어떨까요? 더 이해하기 쉬울 것 같아요!
: 이 모든 것을 종합해보면, SwiftUI와 비슷한 것을 얻을 수 있어요. Regex 리터럴에 대한 훌륭한 대안이 될 수 있겠죠? Swift에서 Regex를 사용하는 것은 좋은 일이에요!!
RegexBuilder 라이브러리는 완전히 새로운 Swift를 제공합니다.
기존 구문보다 사용하기 쉽고 읽기 쉬운 정규식을 위한 UI 스타일 언어입니다.
•
외워야 하는 기호와 약어 대신에 당신이 이해할 수 있는 단어로 표현되어 있습니다.
: Regex builder는 초보자들에게 훌륭하지만, 초보자만을 위한 특징이 아니에요! 강력한 능력을 가지고 있습니다!
Swift를 사용하는 것처럼 Regex을 재사용 가능한 Regex 구성 요소로 바꿀 수 있습니다.
•
다른 Regex에서 사용할 수 있으며, 이러한 구성 요소를 재귀적으로 만들 수도 있습니다.
Regex 빌더들은 몇 Swift 타입을 Regex로 직접 변환되도록 합니다.
Regex builder 중간에 Regex literal를 사용할 수도 있습니다.
•
Regex builder의 명확성과 Regex literal의 간결성 사이에서 균형을 이룰 수 있습니다.
Foundation 날짜 형식 스타일같은 유형의 Regex builder와 사용자 지정 구문 분석 logic를 통합할 수 있으며, 이는 더 풍부한 형식으로 변환할 수 있어요.
어떤 구문을 사용하든 간에 정규식은 사용하기 쉬운 유용한 matching 메소드와 강력한 형식의 capture를 지원합니다.
•
Swift Regex는 Regex 구현에 버금가는 기능 세트를 갖춤 완전히 새로운 오픈 소스 매칭 엔진을 사용합니다.
•
리터럴 구문은 유니코드 Regex 표준과 호환되며 유니코드 정확도가 매우 높습니다.
•
Swift Regex를 사용하려면 macOS 13, iOS 16와 같이 Swift Regex 엔진이 내장된 OS에서 앱이 실행되어야 합니다.
Generic code clarity
개선하기 위해 여러 가지 변경을 가한 곳이 있습니다. 제네릭과 프로토콜입니다.
example) 당신이 git 클라이언트를 쓰고 있고 두 가지 다른 방법으로 메일 맵을 표현해야 한다고 가정
: 커밋을 표시할 때 Dictionary를 사용하여 이름을 빠르게 검색합니다.
: 사용자가 메일 맵을 편집할 수 있도록 허용할 때 배열을 사용하여 항목을 원래 순서로 유지합니다.
: 메일맵 parser가 두 가지 유형에 항목을 추가할 수 있도록 mailmap이라는 프로토콜 사용합니다.
Parser가 메일맵 프로토콜을 사용할 수 있는 두 가지 방법이 있습니다!
*두 함수는 Mailmap의 위치가 미묘하게 다릅니다.
What does “Mailmap” mean?
instance that conform to this protocol | a box which contains an instance that conforms to the protocol |
inheritance list, generic parameter list, generic conformance constraint, opaque result type | variable type, generic argument, generic same-type constraint, function parameter, result type |
상자가 더 많은 공간을 사용 | |
작업하는 데 더 많은 시간이 소요 | |
상자 안에 인스턴스의 모든 기능이 포함되어 있지 않기 때문에 중요 | |
사용하고 있지만 그렇지 않은 곳과 똑같이 생겼기 때문에 박스를 사용하고 있는지는 알 수 없음 |
Swift 5.7은 이 오류를 해결하기 위해서 이런 유형을 포함한 상자를 사용할 때 Swift는 사용자가 any 키워드를 작성하기를 기대합니다.
•
Swift 5.7 이전에 유효했던 코드에서 필수는 아니지만 권장되며 명시적으로 작성하지 않더라도 생성된 인터페이스에서 오류 메세지를 볼 수 있습니다!
오른쪽 열에 있는 모든 항목을 쓰는 방법은 any 키워드를 사용하는 것입니다.
•
그렇게 하면 이 박스들 중 하나를 언제 사용하는 지 알 수 있습니다.
•
any 키워드가 두 함수의 차이를 설명하기 쉽도록 해줬습니다.
◦
addEntries 1은 Generic를 사용하고, addEntries 2는 any type를 사용합니다.
•
any types를 사용하면 오류 메시지가 어떤 문제가 발생했는지 설명하기 쉽습니다.
example)
mergeMailmaps 함수는 메일맵을 일반 메일맵 매개변수로 전달하려고 합니다.
→ 메일맵이 ‘cannot conform to itself’하다는 오류를 만들어냅니다.
원래는 항상 역설적으로 보였지만, 이젠 무슨 일이 일어나고 있는지 명확하게 설명할 수 있습니다.
진짜 문제 : 메일 맵이 들어 있는 상자인 메일 맵이 메일 맵 프로토콜을 준수하지 않는 문제
상자 안에 있는 인스턴스를 통과시키려면
상자를 열고 안에 있는 메일 맵을 꺼내서 대신 통과시켜야 합니다. 하지만 사실, 이런 간단한 경우에는 스위프트가 여러분을 위해 그렇게 할 것입니다. 상자를 열고 상자 안의 인스턴스를 꺼낸 다음 일반 매개 변수에 전달합니다. 따라서 이 오류 메시지는 더 이상 표시되지 않습니다.
: 문제는 메일 맵이 들어 있는 상자인 메일 맵이 메일 맵 프로토콜을 준수하지 않는다는 것입니다. 하지만 이 상자는 여러분이 전달하려고 하는 것이고, 일반 매개변수에 맞지 않습니다.
•
이전에는 프로토콜은 자체 타입을 사용하거나 관련 타입을 가진 경우 또는 Equatable과 같은 프로토콜과 일치하는 경우 어떤 타입으로도 사용될 수 없었습니다.
Swift 5.7에서는 이 오류를 해결했어요!
: 컬렉션과 같은 매우 정교한 프로토콜도 모든 유형으로 사용될 수 있습니다!
: “primary associated types”라는 새로운 기능을 통해 element type를 지정할 수도 있어요!
컬렉션에서 index, iterator, subsequence에 사용하는 유형은 상관하지 않으며 해당 컬렉션에서 지원하는 유형만 사용하면 됩니다!
: 하지만 element는 얘기가 다릅니다.
element를 사용하여 작업을 수행할 수 있으므로 element를 제한하거나 반환해야 합니다.
프로토콜의 거의 모든 사용자가 associated type인 경우, 프로토콜 이름을 각괄호 안에 넣어 기본 유형으로 만들 수 있습니다.
•
모든 위치에 각괄호 구문을 사용하여 프로토콜의 기본 관련 유형을 제한할 수 있습니다.
•
Collection에서 프로토콜의 이름을 쓸 수 있는 거의 모든 위치에서 각괄호 구문을 사용해서 프로토콜의 primary associated type를 제한할 수 있습니다.
: “잠깐만요! 이미 Any Collection이라는 것이 있지 않나요? 그냥 대문자로 된 ‘any’로 실행하는 건가요?”
: “Yes, You’re right, there is!”
오래된 AnyCollection은 any 타입과 동일한 목적으로 제공한 handwritten struct인 type-erasing wrapper입니다.
차이점 : AnyCollection 구조는 여러분이 살면서 본 것 중 가장 지루한 보일러 플레이트 코드
•
any type은 기본적으로 같은 일을 하는 내장된 언어 기능입니다.
•
AnyCollection struct는 이전 버전과의 호환성을 위해, 그리고 아직도 완전히 일치시킬 수 없는 몇 가지 기능들 때문에 계속 유지될 겁니다.
코드에 type-erasing wrapper가 있으면 box class나 closure 대신 built-in any type을 사용하여 구현할 수 있는지 확인하는 것이 좋습니다.
아니면 typealias로 대체할 수도 있습니다.
Improvements to any types
1.
any 키워드가 도입되어 어디에서 사용하는지 확인할 수 있습니다.
2.
Generic 인수로 전달할 수 있습니다. 많은 프로토콜들이 Generic과 함께 사용되는 것을 막았던 제한을 폐지했습니다.
3.
any type를 primary associated type으로 제한할 수 있습니다.
: 이런 개선에도 불구하고, 여전히 한계가 있습니다.
example) 메일맵이 Equatable를 준수할 때
•
메일 맵을 사용할 수 있더라도 equals operator는 두 메일 맵이 모두 같은 type이어야 사용할 수 있습니다.
•
Swift는 많이 개선했지만 기능, 성능 모두에서 중요한 한계를 가지고 있어요.
대신 Generic를 사용해야 합니다.
•
위는 Generic, 아래는 any type를 사용합니다.
•
Generic 버전이 더 효율적이고 더 성능이 좋을 것 같으니, 여러분은 Generic를 사용해야 합니다.
: 여러분은 아마도 any type를 사용하고 싶을겁니다. 왜냐면 읽고 쓰기 훨씬 쉽거든!
Generic version
•
두 개의 Generic 유형 이름을 선언하고 두 이름 모두 제한한 다음 마지막으로 이러한 Generic 이름을 매개 변수 유형으로 사용해야 합니다.
•
“any Collection”, “any Mailmap”에 비해서 지쳐요…
🫶🏻 Swift는 다른 종류들처럼 사용하기 쉬운 Generic를 만들고 있습니다! 🫶🏻
•
Generic 매개변수가 한 곳에서만 사용되는 경우, 이제 some 키워드를 단축형으로 작성할 수 있습니다.
•
primary associated type도 지원하므로 훨씬 이해하기 쉬운 코드를 작성할 수 있습니다.
Generic과 any type 어떤 것이든 선택 가능합니다!
•
“any” 대신 “some”이라고 쓰세요.