스트리밍은 대부분의 브라우저와
Developer 앱에서 사용할 수 있습니다.
-
Swift와 C++ 혼합하기
C++와 Objective-C++ 프로젝트에서 Swift를 사용해 코드를 더 안전하고 빠르고 쉽게 개발할 수 있는 방법을 알아보세요. C++와 Swift API를 사용해 Swift를 앱에 점진적으로 통합하는 방법을 알려드립니다.
챕터
- 0:40 - Basics of interoperability
- 2:22 - Adding Swift to a C++ codebase
- 4:10 - Calling a C++ method in Swift
- 4:50 - Calling a Swift method in C++
- 7:32 - Improving how C++ APIs are imported
- 12:15 - Foreign reference types
리소스
관련 비디오
WWDC23
-
다운로드
♪ ♪
안녕하세요, 조이입니다 Apple 컴파일러 팀의 엔지니어죠 오늘은 Swift와 C++의 상호운용성을 소개할게요 Swift와 C++를 함께 사용하는 Xcode 15의 새로운 기능이죠 이번 세션은 두 부분으로 나눠 진행할 겁니다 먼저 상호운용성의 기본 사항을 설명하겠습니다 이후 Swift에서 자연스럽고 안전하게 C++ API를 만드는 방법을 보여 드릴게요 Swift는 Objective-C로 작성된 대형 앱과 코드베이스가 있는 세상에 출시되었기 때문에 기존 코드를 활용하면서 코드베이스에 점진적으로 도입하도록 해야 했죠 오늘날 Swift는 상호운용성을 한 단계 끌어올려 더 많은 곳에 도입할 수 있게 됐습니다
대규모 C++ 코드베이스를 사용하는 경우 양방향 상호운용성을 활용해 Swift를 점진적으로 도입할 수 있습니다 또한 앱의 C++ 라이브러리에 액세스해야 하는 경우 Objective-C 브리징 레이어를 작성할 필요가 없게 됐어요 샘플 앱을 통해 C++ 코드베이스에서 Swift를 도입하는 일이 얼마나 쉬운지 알아보죠
사진 편집 앱을 만드는 중입니다 카메라 롤에서 사진을 선택하고 색상을 반전하고 밝기를 변경하는 등의 작업을 수행할 수 있죠
코드로 들어가기 전에 앱의 구조를 살펴보겠습니다
앱은 크게 두 부분으로 이루어집니다 이미지 처리 프레임워크와 사용자 인터페이스 코드로요 앱의 기반이 되는 이미지 처리 프레임워크는 C++로 작성되었습니다 사용자 인터페이스 레이어가 C++ 프레임워크와 쉽게 소통할 수 있도록 뷰 컨트롤러를 비롯한 사용자 인터페이스 대부분을 Objective-C++로 구현했죠 이제 앱 사용자가 카메라 롤에서 편집할 사진을 고르도록 설정하고자 합니다 이런 작업을 돕는 새 포토 피커 뷰가 SwiftUI에 있다고 하니 앱에 Swift를 도입해 볼게요 Xcode 15부터 시작하면 Objective-C++ 코드베이스에서 Swift를 쉽게 도입할 수 있고 여전히 모든 C++ API에 액세스할 수 있습니다 프로젝트에 Swift 파일부터 추가해 보죠 C++ 프레임워크를 사용하므로 Xcode가 자동으로 C++ API를 가져옵니다 브리징 헤더는 필요 없어요
그런 다음 프로젝트 빌드 설정에서 C++ 상호운용성을 활성화해야 합니다 Swift가 C와 Objective-C API를 이미 호출하기 때문에 현재 빌드 설정은 C와 Objective-C 모드로 설정돼 있는데 C++로 바꿀 수 있어요
이제 설정이 C++와 Objective-C++로 표시되므로 C++ ImageKit 프레임워크에서 API를 직접 호출할 수 있습니다 Swift 파일로 돌아가 보면 다른 Swift 모듈과 마찬가지로 프레임워크를 가져올 수 있고 Command를 누른 채로 모듈을 클릭해 콘텐츠를 확인할 수 있어요 Swift API처럼 보이지만 C++ ImageKit 라이브러리에서 가져온 겁니다 Swift 컴파일러가 보는 방식이죠 오늘 사용할 API를 살펴봅시다
하단에서 시작하면 CxxImageEngine 유형의 정적 멤버를 볼 수 있습니다 이건 현재 불안전한 포인터로 가져오는데 나중에 자세히 설명할게요 CxxImageEngine에는 다른 멤버도 있습니다 loadImage와 getImages죠 곧 사용할 거예요 C++와 대화하는 메서드 두 가지에 집중할 수 있도록 포토 피커 UI를 모두 드롭인하겠습니다
공유 CxxImageEngine을 가져다가 선택한 이미지마다 loadImage를 호출해 엔진에 로딩할 수 있어요 Swift에서 C++ 메서드 호출이 이렇게나 쉽네요 SwiftUI 뷰가 완성됐으니 Objective-C++ 뷰 컨트롤러에서 사용해 봐야겠죠
Objective-C++ 코드에서 액세스할 수 있도록 구조체를 공용으로 설정할게요
좋습니다, 모든 Swift 코드를 훌륭하게 빌드했어요 이제 뷰 컨트롤러 파일로 이동해 Swift에서 생성한 헤더를 가져올 수 있습니다
모든 공용 Swift API를 포함하는 헤더죠 생성된 헤더를 가져왔으니 Swift 코드를 C++에서 호출할 수 있어요 우선 SwiftUI 뷰를 구축하고 현재 메서드를 호출합니다
Xcode는 코드 완성을 도와주죠
기기 내에서 테스트해 봅시다
앱을 빌드하고 실행했더니 새로운 SwiftUI 뷰를 Objective-C++ 앱으로 곧장 가져오는군요
양방향 상호운용성의 모범 예시를 살펴봤습니다 C++ 유형과 함수를 Swift에서 수월하게 사용할 수 있었고 그 반대도 마찬가지였죠 C++에서 SwiftUI 뷰를 구축하고 사용할 수 있었어요 뷰의 본문은 C++ 프레임워크로 다시 호출됐고요 Swift 컴파일러가 모든 통합을 자동으로 수행하므로 브리징 레이어를 작성할 필요가 없었습니다 모든 API가 직접적이고 네이티브해서 상호운용 되는 여타 언어 대부분과 달리 Swift에서 C++ API를 호출할 때 오버헤드가 발생하지 않았습니다 반대 경우도 마찬가지였고요 방금 작업한 앱은 크기가 꽤 작은 편이었는데 Swift 컴파일러는 크고 복잡한 코드베이스에서도 상호운용성을 지원할 수 있습니다
Swift는 표준 라이브러리 등에서 대부분의 C++ 컬렉션을 가져올 수 있고 함수 템플릿과 클래스 템플릿 특수화를 처리하며 공유 포인터나 유사한 사용자 정의 유형으로 메모리 관리를 지원합니다 또한 이렇게 가져온 API를 높은 수준으로 이해하죠 예를 들어 공유 포인터의 보관 및 릴리즈 작업을 인지하고 이런 고급 지식을 강력한 최적화 스위트에 적용할 수 있습니다
반대로 구조체와 클래스 메서드, 다른 멤버 등 Swift API 대부분을 C++에 활용할 수 있어요 어레이 같은 제네릭 유형이나 시간에 따라 변화하면서 복원력 있는 유형도 활용할 수 있죠 C++ 상호운용성은 Xcode에서 완전히 지원하므로 두 언어 모두 코드 완성과 정의로 이동 디버거 지원이 가능합니다 C++ 상호운용성이 지원하는 API는 지금 보이는 예시보다 훨씬 많아요 Swift 컴파일러는 이렇게 여러 API를 사용하는 대규모 코드베이스 간 상호운용성을 지원해서 언어 사이에 통합적인 경험을 촉진하고 훨씬 더 많은 장소에서 Swift를 도입하도록 합니다 상호운용성의 기본 사항을 알아봤으니 이 기능을 자세히 살펴보고 Swift에서 자연스럽고 안전하게 C++ API를 만드는 방법을 학습해 보도록 하죠 Swift 컴파일러는 C++ API를 자동으로 가져와 안전한 Swift API로 표현할 수 있습니다 예를 들어 기본적으로 C++ 유형은 Swift 구조체로 가져오고 C++ 연산자를 유사한 Swift 연산자에 매핑하며 컨테이너는 자동으로 컬렉션으로 가져옵니다 하지만 컴파일러를 사용하면 API를 가져오는 방식을 미세 조정하고 훨씬 더 자연스러운 API를 활용할 수도 있어요 주석을 사용해 자세한 API 정보를 컴파일러에 제공하면 됩니다
예를 들어 함수나 메서드가 사용하는 C++ 명명 규칙이 Swift에서 부자연스럽다면 주석을 통해 가져온 함수의 이름을 바꾸거나 인수 레이블을 추가하거나 getter 또는 setter를 연산 프로퍼티로 추가할 수 있죠
또한 주석을 사용해 참조 시맨틱 같은 고급 패턴을 설명하고 일부 유형을 Swift 클래스로 가져올 수도 있습니다
별다른 문제가 없는 API를 안전하지 않다고 인식한다면 Swift를 수정할 수도 있죠
주석은 Swift가 가져온 API의 정보를 전달하는 강력한 방법입니다 샘플 앱에서 사용하는 API를 확인해 보고 Swift가 안전하고 직관적인 방식으로 API를 가져오도록 주석을 사용하는 방법을 알아보죠
포토 피커는 완료했으니 편집한 사진을 사진 보관함에 저장하려면 저장 버튼을 추가해야겠죠
Swift로 되돌아가면 가져온 API를 전부 다시 볼 수 있습니다
먼저 저장할 사진을 모아야 하는데요 getImages 함수를 사용하면 됩니다
getImages 함수는 C++ 벡터를 반환해요 이 메서드를 호출하기 전에 Swift에서 벡터의 작동 방식을 자세히 알아보겠습니다 Swift 유형은 값 유형과 참조 유형 이렇게 두 가지입니다 Swift에서 구조체는 값 유형을 클래스는 참조 유형을 표현하죠
기본적으로 C++ 유형은 Swift에서 값 유형으로 가져와요
따라서 Swift는 값 유형으로 벡터를 가져오고 해당 유형은 Swift 구조체처럼 동작할 겁니다 벡터와 다른 Swift 구조체의 유일한 차이점은 Swift가 복사 생성자 같은 특수 멤버를 사용해 수명을 관리한다는 점입니다 이런 복사 생성자는 종종 전체 복사를 수행합니다 수정될 때만 복사되는 Swift 어레이와 달리 Swift가 벡터를 복사하면 모든 요소가 복사되는 거죠
이제 이미지 벡터가 있으므로 for 루프에서 벡터를 반복해 각 이미지를 가져오고 이미지를 다시 uiImage로 변환한 뒤 사진 보관함에 저장합니다
for 루프가 작동하는 건 벡터에 있는 begin과 end 메서드 덕분이죠 Swift는 자동으로 이를 컬렉션으로 가져옵니다 컬렉션에 대한 자동 준수를 통해 벡터를 Swift 어레이로 쉽게 변환하고 map과 filter를 비롯한 메서드에 액세스를 제공합니다 안전을 위해서는 Swift 안전 모델에 맞지 않는 C++ 반복자 API보다 이런 Swift 컬렉션 API를 사용해야겠죠
C++ 반복자를 사용하면 수명 관련 문제나 무효화 메모리 액세스 등 버그가 발생하기 쉽습니다 반면 Swift 컬렉션 API는 100% 안전하죠 C++ 컬렉션에서 작동할 때조차요
Swift 컴파일러는 안전하지 않은 C++ API를 사용할 수 없다고 표시하며 더 안전한 대안을 제안해 안전한 API로 안내해요 Swift 앱으로 돌아가 봅시다 애로 사항이 한 가지 있어요 CxxImageEngine을 사용할 때마다 불안전한 포인터라 신경 쓰이네요 사실 Swift와 C++ 둘 다 항상 이 유형을 포인터로 사용합니다 참조 시맨틱이 있기 때문이죠 해당 유형이 객체 아이덴티티를 가지며 복사본은 별개의 값이 아니라 동일한 메모리에 대한 공유 참조가 된다는 뜻입니다 앞서 언급했듯이 Swift 유형은 두 가지입니다 값 유형과 참조 유형이죠 또한 Objective-C는 값 유형과 참조 유형을 명확하게 구분하므로 Objective-C 유형을 구조체와 클래스에 쉽게 매핑할 수 있습니다 C++는 어떤 유형이 어떤 범주에 속하는지 뚜렷하게 알 수 없어요 Swift나 Objective-C와 달리 값 유형과 참조 유형을 명확하게 구분하지 않거든요
따라서 컴파일러는 기본적으로 모든 걸 값 유형으로 가져옵니다 반면 Swift는 C++ 코드에 주석을 추가해 참조 또는 클래스 유형으로 일부 항목을 가져오는 옵션도 제공합니다
SWIFT_SHARED_REFERENCE 속성으로 CxxImageEngine을 Swift 클래스에 매핑할 수 있죠 해당 속성은 유형이 항상 포인터 또는 참조로 전달되고 불안전한 포인터가 아닌 유형으로 이런 간접 조치를 표현하도록 Swift에서 강제한다는 뜻입니다
코드를 안전하게 보호하고자 Swift는 필요에 따라 참조를 보관하거나 릴리즈해서 참조의 수명을 자동으로 관리해요 이런 참조 횟수 계산 방식을 활성화하려면 retain과 release 함수를 Swift에 모두 제공해야 합니다 이제 C++ ImageKit 헤더를 자세히 알아봅시다
SWIFT_SHARED_REFERENCE 등 주석에 액세스하려면 swift/bridging을 가져오면 됩니다 이제 주석을 유형에 적용하고 Swift가 호출할 수 있는 retain과 release 함수를 명시해요 좋습니다 Swift 컴파일러 오류가 뜨면서 이제 포인터를 역참조할 필요가 없다고 알려 주는군요
Swift에서 C++ API를 편안하게 사용할 수 있도록 마지막으로 하나 더 작업해 볼게요 for 루프에서 getImages를 호출합니다 getter와 setter를 이렇게 정의하는 건 C++에서 아주 일반적인 패턴이지만 Swift에서는 좀 어색합니다 Swift에서 네이티브처럼 느껴지도록 swift/bridging에서 또 다른 주석을 사용할게요 SWIFT_COMPUTED_PROPERTY 속성을 getter와 setter에 적용해 Swift 연산 프로퍼티로 해당 페어를 매핑하겠습니다 C++ 헤더로 돌아가서 주석을 적용해 봅시다
getImages 메서드의 호출자를 검색할게요 정의에서 우클릭해 Swift 호출자를 선택합니다 이제 간단하게 '이미지'로 이름을 바꿀 수 있습니다
아주 좋아요 마지막으로 앱을 테스트할게요
사진 몇 장을 선택해 카메라 롤에 재저장할 수 있네요
훌륭해요 이번 시간에는 주석 두 개만으로 API를 가져오는 방식을 개선해 봤지만 C++ 헤더에서 사용할 수 있는 주석은 아주 다양합니다 swift/bridging을 가져오면 바로 액세스할 수 있죠
Xcode 15에서 C++ 상호운용성을 활성화하려면 상호운용성 모드를 C와 Objective-C에서 C++와 Objective-C++로 바꾸세요 Swift와 C++ 상호운용성은 모든 Apple 플랫폼과 Linux, Windows에서 지원합니다 C++는 크고 복잡한 언어이므로 여러분의 피드백을 참고해 C++ API를 가져오고 Swift API를 활용하는 방법을 개선하고자 합니다 C++ API를 가져오는 방식을 변경하면 새로운 버전의 상호운용성을 생성하게 돼요 새로운 기능을 언제 사용할지 선택할 수 있으므로 개발에서 C++ API를 자신 있게 활용할 수 있겠죠
문제가 발생하거나 제안하고 싶은 사항이 있다면 언제든 환영입니다 피드백 지원을 통해 말씀해 주세요 C++ 상호운용성은 전적으로 오픈 소스에서 Swift 컴파일러 작업 그룹이 디자인했습니다 십수 개가 넘는 회사와 학교에서 온 엔지니어와 학생들로 구성된 팀이죠 이 작업 그룹은 Swift와 C++ 상호운용성의 미래 비전을 정의하고 차후 발전된 기능을 안내할 문서 두 가지를 개발했습니다
작업 그룹에 가입하고 포럼에 참여하고 싶다면 swift.org를 방문해 보세요
Swift 5.9에서는 C++ API를 오버헤드 없이 자동으로 안전하게 사용할 수 있습니다 새로운 Swift 코드를 C++에 다시 활용해서 Swift를 점진적으로 도입할 수 있어요 컴파일러에 더 많은 정보를 제공해 가져온 API를 개선하고 미세 조정하세요
시청해 주셔서 감사합니다 모든 C++ 코드베이스에 Swift를 도입해 보세요
-
-
4:10 - Calling a C++ method from Swift
func loadImage(_ image: UIImage) { // Load an image into the shared C++ class. CxxImageEngine.shared.pointee.loadImage(image) }
-
4:20 - Import a C++ framework
import CxxImageKit
-
4:45 - Import the Generated Header
#import "SampleApp-Swift.h"
-
4:57 - Calling a Swift method in C++
- (IBAction)openPhotoLibrary:(UIButton *)sender { // Construct SwiftUI view SampleApp::ImagePicker::init().present(self); }
-
8:22 - Using the SWIFT_COMPUTED_PROPERTY attribute
int getValue() const SWIFT_COMPUTED_PROPERTY; void setValue(int newValue);
-
8:42 - Using the SWIFT_SHARED_REFERENCE attribute
struct SWIFT_SHARED_REFERENCE(retain, release) CxxReferenceType;
-
8:52 - Using the SWIFT_RETURNS_INDEPENDENT_VALUE attribute
SWIFT_RETURNS_INDEPENDENT_VALUE std::string_view networkName() const;
-
10:45 - Using a for-loop to iterate over a C++ std::vector in Swift
// Get every image out of the shared C++ class. for image in CxxImageEngine.shared.pointee.getImages() { let uiImage = CxxImageEngine.shared.pointee.uiImageFrom(image) UIImageWriteToSavedPhotosAlbum(uiImage, nil, nil, nil) }
-
13:54 - Import swift/bridging
#import <swift/bridging>
-
14:01 - Applying the SWIFT_SHARED_REFERENCE attribute to CxxImageEngine
struct SWIFT_SHARED_REFERENCE(IKRetain, IKRelease) CxxImageEngine { // ... };
-
14:53 - Applying the SWIFT_COMPUTED_PROPERTY attribute to getImages
/// \returns all images that have been loaded into the engine. Includes any modifications that were /// applied to the images. SWIFT_COMPUTED_PROPERTY inline std::vector<Image *_Nonnull> getImages() const;
-
15:06 - Updated for-loop using the "images" computed property
// Get every image out of the shared C++ class. for image in CxxImageEngine.shared.pointee.images { let uiImage = CxxImageEngine.shared.pointee.uiImageFrom(image) UIImageWriteToSavedPhotosAlbum(uiImage, nil, nil, nil) }
-
-
찾고 계신 콘텐츠가 있나요? 위에 주제를 입력하고 원하는 내용을 바로 검색해 보세요.
쿼리를 제출하는 중에 오류가 발생했습니다. 인터넷 연결을 확인하고 다시 시도해 주세요.