Swift는 이미 Objective-C 기반으로 되어있는 수많은 앱이 있는 상태에서 등장했기 때문에 기존 코드 베이스와 함께 쓸 수 있는 기능이 필수적이였다.
이제 C++ 코드베이스를 가진 곳에서도 Swift를 점진적으로 적용할 수 있다.
Swift에서 C++ 라이브러리를 쓸 수 있다.
C++ 프레임워크도 그냥 자연스럽게 import하면 된다.
import CxxImageKit
호출도 자연스럽게
다만 그냥 가져오면 unsafepointer형태로 import가 되어버린다.
func loadImage(_ image: UIImage) {
// Load an image into the shared C++ class.
CxxImageEngine.shared.pointee.loadImage(image)
}
Objective-C++쪽에서는 generated된 헤더를 import하면 된다.
#import "SampleApp-Swift.h"
사용도 쉽게
- (IBAction)openPhotoLibrary:(UIButton *)sender {
// Construct SwiftUI view
SampleApp::ImagePicker::init().present(self);
}
이 상호 운용은 양방향이고, 컴파일러에 의해 자동적으로 이뤄진다.
모든 호출은 직접 이뤄지기 때문에 오버헤드가 없다.
C++ → Swift
Swift → C++
Xcode에서 지원
Swift 가 C++ API를 가져오는 규칙: 가져와서 안전하게 만들 수 있어야 한다.
C++ 타입은 기본적으로 struct로 가져온다.
C++ 연산자는 비슷한 Swift연산자로 매핑된다.
컨테이너는 Collection으로 매핑된다.
다만 컴파일러에 추가 어노테이션을 좀 더 주면 좀 더 자연스럽게 쓸 수 있게 가져와준다.
계산 프로퍼티의 accessor
int getValue() const SWIFT_COMPUTED_PROPERTY;
void setValue(int newValue) SWIFT_COMPUTED_PROPERTY;
var value: Int { get set }
reference semantic
struct SWIFT_SHARED_REFERENCE(retain, release) CxxReferenceType;
class CxxReferenceType
안전하지 않은 API를 가져오기
SWIFT_RETURNS_INDEPENDENT_VALUE
std::string_view netwrokName() const;
func networkName() -> std.string_view
std::vector in Swift
C++ 타입은 기본적으로 값 타입으로 import 된다. 그래서 std::vector도 값타입이다.
차이점은 Swift는 해당 타입의 특수한 멤버인 복사 생성자를 써서 생명주기를 관리한다는 것이다.
vector에 대해서 for-loop를 돌릴 수 있다.
// 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)
}
이는 vector가 begin과 end 메소드를 가지고 있기 때문으로, 이 두가지를 가지고 있으면 어떤 타입이던 자동적으로 Swift collection으로 import된다.
C++ iterator API보다는 Swift Collection API를 쓰는 게 안전하다.
Iterator는 생명주기 이슈나 잘못된 메모리 접근 등이 쉽게 발생한다.
Collection API를 쓰면 C++콜렉션을 쓰더라도 안전을 보장해준다.
그래서 이것도 막아준다.
참조타입
C++에는 값타입과 참조 타입의 구분이 명확하지 않기 때문에, Swift는 모든 타입을 값타입으로 일단 가져온다.
참조타입으로 가져오기 위해서는 C++ 코드 쪽에서 annotation을 넣어줘야 한다.
#import <swift/bridging>
// 커스텀 retain, release 메소드
void IKRetain(CxxImageEngine *_Notnull engine);
void IKRelease(CxxImageEngine *_Notnull engine);
struct SWIFT_SHARED_REFERENCE(IKRetain, IKRelease) CxxImageEngine {
// ...
};
이러면 unsafePointer indirection을 없앨 수 있다.
계산 프로퍼티 만들기
/// \\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;
완성본
// Get every image out of the shared C++ class.
for image in CxxImageEngine.shared.images {
let uiImage = CxxImageEngine.shared.pointee.uiImageFrom(image)
UIImageWriteToSavedPhotosAlbum(uiImage, nil, nil, nil)
}
이 외에도 많은 annotation을 지원한다.(헤더)