0Pricing
Swift Academy · 강의

UnsafePointer와 UnsafeMutablePointer

원시 메모리를 주의해서 읽고 씁니다.

UnsafePointer와 UnsafeMutablePointer은(는) CoddyKit의 무료 Swift Academy 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Swift Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Swift Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

Swift의 형식 지정 pointer

Swift의 안전한 참조는 원시 주소를 노출하지 않지만, C와 상호 운용하거나 저수준 데이터 구조를 만들 때는 읽기 전용 액세스에 UnsafePointer<T>를, 읽기/쓰기에 UnsafeMutablePointer<T>를 사용합니다.

형식 지정 pointer는 요소 유형을 알고 있으므로 stride를 계산하고 올바른 유형의 값을 읽을 수 있습니다.

// A pointer to an Int we can read but not write
func readFirst(_ p: UnsafePointer<Int>) -> Int {
    return p.pointee
}

포인터가 가리키는 값 속성

pointee는 포인터가 현재 가리키는 값입니다. 가변 pointer에서는 값을 읽고 할당할 수 있지만, 읽기 전용 pointer에서는 읽기만 할 수 있습니다.

초기화되지 않았거나 할당 해제된 메모리에서 pointee에 접근하면 정의되지 않은 동작이 발생하며, 유형 시스템이 이를 감지하지 못합니다.

let p = UnsafeMutablePointer<Int>.allocate(capacity: 1)
p.initialize(to: 42)
print(p.pointee)   // 42
p.pointee = 99
print(p.pointee)   // 99

메모리 할당하기

UnsafeMutablePointer<T>.allocate(capacity:)는 capacity개 요소를 위한 공간을 예약하지만 초기화하지는 않습니다. 메모리는 raw 상태이므로 초기화하기 전에 읽는 것은 허용되지 않습니다.

// Reserve space for 3 Doubles (uninitialized)
let buffer = UnsafeMutablePointer<Double>.allocate(capacity: 3)
// Must initialize before reading any element

initialize와 deinitialize

읽기 전에 initialize해야 합니다. 클래스나 문자열처럼 정리가 필요한 유형을 할당 해제하기 전에는 리소스를 해제하도록 deinitialize해야 합니다.

수명 주기는 다음과 같습니다: allocate → initialize → 사용 → deinitialize → deallocate.

let p = UnsafeMutablePointer<String>.allocate(capacity: 1)
p.initialize(to: "hello")
print(p.pointee)
p.deinitialize(count: 1) // releases the String
p.deallocate()

allocate와 deallocate는 항상 함께 사용하기

모든 allocate는 정확히 한 번의 deallocate와 짝을 이루어야 하며, 그렇지 않으면 메모리 누수가 발생합니다. 할당 직후 지연 실행 구문을 사용하는 것이 가장 안전한 패턴입니다. 함수가 어떻게 종료되든 실행되기 때문입니다.

func process() {
    let p = UnsafeMutablePointer<Int>.allocate(capacity: 1)
    defer { p.deallocate() }
    p.initialize(to: 7)
    print(p.pointee)
} // deallocate runs here automatically

pointer 산술 연산

형식 지정 pointer는 바이트가 아니라 요소 단위로 인덱싱과 산술 연산을 지원합니다. p + 2는 T의 stride 두 개만큼 이동합니다. p[i]는 (p + i).pointee를 줄여 쓴 표현입니다.

let p = UnsafeMutablePointer<Int>.allocate(capacity: 3)
p.initialize(repeating: 0, count: 3)
p[0] = 10
p[1] = 20
p[2] = 30
print((p + 1).pointee) // 20
p.deinitialize(count: 3)
p.deallocate()

여러 요소 initialize하기

메모리 블록에서 initialize(repeating:count:)는 모든 슬롯을 같은 값으로 설정하고, initialize(from:count:)는 다른 버퍼에서 값을 복사합니다. 두 방법 모두 범위를 완전히 초기화하므로 안전하게 읽을 수 있습니다.

let dst = UnsafeMutablePointer<Int>.allocate(capacity: 4)
dst.initialize(repeating: 5, count: 4)
print(dst[3]) // 5
dst.deinitialize(count: 4)
dst.deallocate()

읽기 전용 pointer와 가변 pointer 비교

데이터를 확인하기만 하는 함수는 UnsafePointer<T>를 받아야 하고, 데이터를 수정하는 함수는 UnsafeMutablePointer<T>를 받아야 합니다. 가변 pointer는 읽기 전용 pointer로 암시적으로 변환할 수 있지만 그 반대는 불가능하므로, API 경계에서 의도를 강제할 수 있습니다.

func sum(_ p: UnsafePointer<Int>, _ n: Int) -> Int {
    var total = 0
    for i in 0..<n { total += p[i] }
    return total
}
// A mutable pointer can be passed here directly

로컬 값에 withUnsafePointer 사용하기

할당 없이 기존 값에 대한 임시 pointer를 얻으려면 withUnsafePointer(to:) 또는 가변 형식을 사용하십시오. pointer는 클로저 내부에서만 유효하므로 절대 외부로 유출해서는 안 됩니다.

var value = 100
withUnsafeMutablePointer(to: &value) { ptr in
    ptr.pointee += 1
}
print(value) // 101

흔한 함정

형식 지정 pointer는 강력하지만 실수에 관대하지 않습니다:

  • pointee를 initialize하기 전에 읽으면 정의되지 않은 동작이 발생합니다.
  • 리소스를 보유하는 유형에서 deinitialize를 잊으면 누수가 발생합니다.
  • withUnsafePointer의 pointer를 외부로 유출하면 유효하지 않은 pointer가 됩니다.

이 부분에서는 컴파일러가 여러분을 신뢰하므로 런타임 안전장치가 없습니다.

// DANGEROUS — pointer escapes the closure
// var escaped: UnsafePointer<Int>?
// withUnsafePointer(to: &x) { escaped = $0 }
// escaped!.pointee  // dangling, undefined behavior

작은 수동 stack

이제 모든 내용을 결합해 보겠습니다. 블록을 할당하고, 인덱싱을 사용해 요소로 다룬 다음, 정리합니다. 이는 사용자 지정 컬렉션과 C 상호 운용의 기반입니다.

let n = 3
let stack = UnsafeMutablePointer<Int>.allocate(capacity: n)
stack.initialize(repeating: 0, count: n)
for i in 0..<n { stack[i] = i * i }
// stack now holds 0, 1, 4
stack.deinitialize(count: n)
stack.deallocate()

빠른 확인

올바른 pointer 수명 주기를 떠올려 보십시오.

요약

이제 형식 지정 pointer를 알게 되었습니다:

  • UnsafePointer는 읽기만 하고, UnsafeMutablePointer는 읽고 씁니다.
  • pointee로 값에 접근하며, 산술 연산은 요소 단위로 수행됩니다.
  • 수명 주기는 allocate → initialize → 사용 → deinitialize → deallocate이며, 지연 실행 구문으로 보호하는 것이 좋습니다.

다음에는 버퍼 pointer가 전체 연속 영역을 컬렉션 친화적인 API로 감싸는 방법을 살펴봅니다.

자주 묻는 질문

“UnsafePointer와 UnsafeMutablePointer” 강의는 무료인가요?

네 — “UnsafePointer와 UnsafeMutablePointer” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Swift Academy 강의 전체를 잠금 해제할 수 있습니다. Swift Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“UnsafePointer와 UnsafeMutablePointer”에서 뭘 배우나요?

원시 메모리를 주의해서 읽고 씁니다. 브라우저에서 직접 실행하는 실습 코드로 Swift Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Swift Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Swift Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.

“UnsafePointer와 UnsafeMutablePointer” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Swift Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Swift Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. MemoryLayout과 정렬
  2. UnsafePointer와 UnsafeMutablePointer
  3. 안전하지 않은 버퍼 포인터
  4. withUnsafeBytes와 C 상호 운용
← Swift Academy(으)로 돌아가기