Unmanaged
를 UnsafePointer
로 변경하기Swift 표준 라이브러리의 Unmanaged<Instance>
구조체는 ARC에 참여하지 않는 타입 안전 객체 래퍼를 제공한다. 이를 통해 사용자가 수동으로 retain/release 호출을 할 수 있다.
관련 자료:
Swift Evolution 토론,
리팩토링 제안 토론,
리뷰
Unmanaged 타입으로 변환하거나 Unmanaged 타입에서 변환하기 위해 다음과 같은 메서드를 제공한다:
static func fromOpaque(value: COpaquePointer) -> Unmanaged<Instance>
func toOpaque() -> COpaquePointer
그러나 void *
나 const void *
를 받는 C API는 Swift에서 COpaquePointer
대신 UnsafePointer<Void>
나 UnsafeMutablePointer<Void>
로 노출된다. 실제로 사용자는 UnsafePointer
→ COpaquePointer
→ Unmanaged
로 변환해야 하며, 이로 인해 다음과 같은 복잡한 코드가 발생한다:
someFunction(context: UnsafeMutablePointer(Unmanaged.passUnretained(self).toOpaque()))
info.retain = { Unmanaged<AnyObject>.fromOpaque(COpaquePointer($0)).retain() }
info.copyDescription = {
Unmanaged.passRetained(CFCopyDescription(Unmanaged.fromOpaque(COpaquePointer($0)).takeUnretainedValue()))
}
Unmanaged
API에서 COpaquePointer
사용을 UnsafePointer<Void>
와 UnsafeMutablePointer<Void>
로 대체한다.
영향을 받는 함수는 fromOpaque()
와 toOpaque()
다. 현재 구현에서 매우 사소한 수정만 필요하다:
@_transparent
@warn_unused_result
public static func fromOpaque(value: UnsafePointer<Void>) -> Unmanaged {
// 널 포인터 검사는 디버그 검사로, 특정 잘못된 포인터 값을 방어한다.
_debugPrecondition(
value != nil,
"널 포인터에서 Unmanaged 인스턴스를 생성하려고 시도했습니다.")
return Unmanaged(_private: unsafeBitCast(value, Instance.self))
}
@_transparent
@warn_unused_result
public func toOpaque() -> UnsafeMutablePointer<Void> {
return unsafeBitCast(_value, UnsafeMutablePointer<Void>.self)
}
UnsafeMutablePointer
타입의 값은 UnsafePointer
나 UnsafeMutablePointer
를 받는 함수에 모두 전달할 수 있다. 따라서 간단하고 사용하기 쉽게 fromOpaque()
의 입력 타입으로 UnsafePointer
를, toOpaque()
의 반환 타입으로 UnsafeMutablePointer
를 선택한다.
위 예제 사용법은 더 이상 변환을 필요로 하지 않는다:
someFunction(context: Unmanaged.passUnretained(self).toOpaque())
info.retain = { Unmanaged<AnyObject>.fromOpaque($0).retain() }
info.copyDescription = {
Unmanaged.passRetained(CFCopyDescription(Unmanaged.fromOpaque($0).takeUnretainedValue()))
}
이전에 COpaquePointer
를 사용해 Unmanaged
API를 호출하던 코드는 UnsafePointer
를 사용하도록 변경해야 한다. COpaquePointer
버전은 전환을 돕기 위해 다음과 같은 가용성 속성을 유지할 수 있다:
@available(*, unavailable, message="fromOpaque(value: UnsafeMutablePointer<Void>)를 대신 사용하세요")
@available(*, unavailable, message="toOpaque() -> UnsafePointer<Void>를 대신 사용하세요")
COpaquePointer
를 사용하는 코드는 이 타입에 크게 의존하지 않는 것으로 보이며, 이 변경으로 인해 큰 영향을 받지 않을 것이다.
COpaquePointer
는 더 이상 필요 없는 잔재로 여겨지며, C API와의 더 나은 연결이 필요하다. 따라서 이 방향으로 나아가는 것이 바람직하다.