Swift – Interacting with C Pointers

https://developer.apple.com/swift/blog/?id=6

Objective-C 및 C의 API들은 종종 포인터를 사용한다. Swift의 데이터 타입들은 포인터 기반의 Cocoa API들과 자연스럽게 어울리도록 디자인되었고, Swift는 포인터를 파라미터로 일부 다루고 있다.

포인터와 in/out 파라미터

C와 Objective-C의 함수는 복수의 값을 리턴하지 못하기 때문에 부가정보를 함수 외부로 내보내기 위해서 포인터를 사용한다. Swift는 이런 경우 inout 어트리뷰트를 가진 파라미터를 정의할 수 있어서 Swift에서는 & 문법을 사용하여 var 변수의 포인터를 넘겨줄 수 있다. 예를 들어 UIColorgetRed(_:green:blue:alpha:) 메소드는 4개의 CGFloat* 포인터를 받는데 이는 다음과 같이 사용하여 4개의 변수에 대해 각 컬러 값을 얻어낼 수 있다.

하지만 swift는 tuple을 지원하므로 순수 swift로만 작성된 코드에서는 흔히 쓰이지 않을 것이다. Xcode 6.1 이후부터는 swap(&a, &b) 대신에 a, b = b, a를 쓰는 것으로도 두 변수의 값을 교환할 수 있으며, 같은 방법1으로 하나의 함수가 여러 개의 값을 동시에 리턴하도록 하는 것도 가능하다.

var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0
color.getRed(&r, green:&g, blue: %b, alpha: &a)

다른 흔한 용례는 NSError 구문이다. 많은 메소드들에서 실패 시 정볼르 기록해주기 위해 NSError** 이중 포인터를 사용한다. 예를 들어 NSFileManagercontentsOfDirectoryAtPath(_:error:_) 메소드는 잠재적인 에러를 NSError? 변수에 저장한다.

var maybeError: NSError?
if let contents = NSFileManager.defaultManager().contentsOfDirectoryAtPath("/usr/bin", error: &maybeError) {
    // 컨텐츠 사용
} else if let error = maybeError {
    // 에러 처리 
}

안전을 위해 Swift는 포인터 문법(&)을 사용하기 전에 변수의 값을 초기화할 것을 요구한다. 이는 함수 내부에서 포인터에 값을 쓰기 이전에 읽을 수 있기 때문이다.

배열 파라미터로서의 포인터

포인터는 특히 C의 배열과 깊은 관련을 갖는다. Swift는 배열 자체를 포인터로 C API에 적용할 수 있다. 예를 들어 변경 불가능한 배열 값은 const T* 타입의 포인터로 전달 가능하며, 변경 가능한 배열의 경우 & 문법으로 전달가능하다.

import Accelerate
let a: [Float] = [1,2,3,4]
let b: [Float] = [0.5, 0.25, 0.125, 0.0625]
var result: [Float] = [0, 0, 0, 0]
vDSP_vadd(a, 1, b, 1, &reslut, 1, 4)
// --> [1.5, 2.25, 3.125, 4.0625]

문자열 파라미터

C는 문자열 파라미터로 주로 const char * 타입을 사용하는데 Swift의 String 타입은 여기에 그대로 들어갈 수 있다. C API로 넘겨질 때 문자열은 null 문자로 종료하는 UTF-8 인코딩으로 전달된다. 예를 들어 C나 POSIX 라이브러리 함수를 쓸 때 다음과 같은 사용이 가능하다.

puts("Hello from libc")
let fd = open("/tmp/scratch.txt", O_WRONLY|O_CREAT, 0x666)
if fd < 0 {
    perror("could not open /tmp/scratch.txt")
} else {
    let text = "Hello world"
    write(fd, text, strlen(text))
    close(fd)
}

포인터 인자 변환시 안정성

Swift는 많은 경우 C API와 자연스럽게 연결될 수 있도록 잘 동작하지만 기본적으로 C 포린터와의 인터랙션은 불안정한 부분을 내재한다. 특히 다음과 같은 부분에서는 주의가 필요하다.

  • 이러한 변환은 피호출자가 포인터의 값을 나중에 사용하기 위해 저장하는 부분에서 안전하지 않다. 이 변환은 내부적인 부분으로 함수 호출 시에만 유요할 수 있다. 따라서 반복적ㅇ으로 동일한 값을 전달하더라도 매번 다른 포인터를 함수가 전달받을 확률이 크다. 단 전역이나 정적 변수의 경우에는 예외적이다.
  • 배열이나 문자열의 경계 체크가 되지 않는다. 따라서 인자로 넘기기 전에 그 크기를 충분히 염두에 두어야 한다.

  1. 대입식의 좌,우변에 각각 튜플을 두어 튜플의 각 요소들을 한 구문에서 한꺼번에 대입하도록 하는 방법.