OpaquePointer
OpaquePointer
는 Swfit2.x 에서 COpaquePointer
가 이름이 바뀐 타입으로 불투명한1 C 포인터를 감싸는 래퍼 타입이다.1 타입을 반입할 수 있는 구조체의 포인터라면 원 구조체 Tc
타입을 Swift 로 반입할 수 있고, 이 경우에는 UnsafePointer<Tc>
의 형태로 쓸 수 있다. 하지만 원 타입을 Swift가 이해할 수 없다면, 그 타입의 크기를 결정할 수 없으므로 이에 대한 포인터 역시 Swift 내부에서 결정하는 것은 어렵다.
물론 UnsafePointer<Void>
타입을 이용하는 것도 가능하다 생각할 수 있지만, 이 경우에는 C에서 void *
타입이며, 어찌됐든 pass 시점에 캐스팅해야 하는 한계가 있다. (그리고 원 타입을 알 수 없으면 캐스팅도 불가능하다.)
init:
은 다음과 같은 인자들을 받을 수 있다. (다만 실제로 생성할 일이 있을지는….)
UnsafeMutablePointer<T>
,UnsafeMutablePointer<T>?
UnsafePointer<T>
,UnsafePointer<T>?
(bitpattern: Int)
,(bitpattern: UInt)
UnsafeRawPointer
,UnsafeRawPointer?
UnsafeMutableRawPointer
,UnsafeMutableRawPointer?
UnsafeRawPonter
는 타입이 지정되지 않은 데이터를 액세스하는 raw 포인터 타입이며, 사실상 C의void *
타입 포인터를 날 것 그대로 엑세스하는 것과 유사하다.
UnsafePointer
패밀리와의 차이
UnsafePointer<T>
, UnsafeMutablePointer<T>
는 사실상 독립타입이 아닌 특정 Swift 타입에 대한 포인터 래퍼의 개념이므로, 이는 Swift 타입에 대한 포인터여야 한다. 기본적으로 C의 원시타입은 대부분 Swift 의 타입과 맵핑이 가능하지만, C의 구조체는 Swift의 구조체와 다르다. 따라서 UnsafePointer<T>
패밀리에 속하는 타입들은 C-API의 배열이나 포인터를 다루는 API와 연계할 때 사용하며, OpaquePointer
는 알 수 없는 구조체 타입에 대한 포인터로 사용된다.
예시
sqlite3
의 API를 Swift 내에서 이용하는 상황을 가정해보자.2
데이터베이스에 접근하기 위해서는 연결에 대한 핸들러가 필요한데, sqlite3의 API에서 sqltie_open()
함수는 데이터베이스 파일의 경로문자열을 받아서 해당 파일을 열고, 그 연결 핸들러를 리턴한다. 이 때 핸들러는 void *
로 캐스팅된 sqlite3
객체 포인터이며, 따라서 이를 사용하기 위해서는 OpaquePointer
를 써야 한다.
class DBConnector {
lazy var db: OpaquePointer = { [unowned self] in
let _db: OpaquePointer? = nil
if sqlite3_open(self.db_path, &_db) == SQLITE_OK {
return _db!
}
return nil
}()
}
- 여기서 불투명한 C 포인터란, Swift로 반입될 수 없는 구조를 가진 C 구조체를 말한다. ↩
- 이 내용은 Swift에서 Sqlite3 사용하기 글에서도 다뤄진다. ↩
- 이름만 바뀐 것 외에도
nil
값을 갖기 위해서는 명시적으로 옵셔널로 선언해야 하는 차이도 있다. 이는UnsafePointer
계열의 타입도 마찬가지다. ↩