반응형
👋 들어가기 전
최근 일과 사이드 프로젝트에 바쁘게 생활하고 있어, 기저 개념을 학습할 수 있는 시간과 기회가 부족했는데
팀동료께서 좋은 개념을 설명해주셔서 한번 학습해보려고 한다.
주제는 바로 제목과 같이 소유권이라는 개념과 함꼐 Swift 진영에서는 어떻게 다루고 있는 지 살펴보려고한다.
🏁 학습할 내용
- 소유권은 무엇이고, 왜 필요한가?
- Copyable과 ~Copyable
- borrowing
- consume
- consuming
- SIL 맛보기
🧐 소유권은 무엇이고, 왜 필요한가?
1️⃣ 소유권
아래 코드가 있을 떄, 메모리 관점에서 다음 지문들을 생각해보자.
let a = Data(...)
let b = a
a ----+
|
↓
Data
↑
|
b ----+
- 누가 Owner인가?
- 언제 Owner가 바뀌는가?
- 동시에 접근 가능한가?
- 복사가 일어나는가?
- 이동(move)이 일어나는가?
이런 내용들을 정의하는 개념이 바로 소유권이다.
그러면 왜 필요할까?
소유권은 해당 데이터가 메모리에 얼마나 머물 수 있는 지에 대한 생명주기와 깊은 연관이 있다.
2️⃣ 키워드
키워드를 통해, 소유권에 대한 세부 개념을 살펴보자.
🖨️ Copyable과 ~Copyable
- Copyable
- 타입의 인스턴스는 자유롭게 복사할 수 있다를 보장하는 프로토콜
- 암묵적으로 대부분의 타입이 Copyable
- ~Copyable
- 반대로 복사가 불가능한 타입
- 인스턴스 생명주기가 명확히 단 하나로 추적되므로, 구조체임에도 deinit 사용 가능
- 시스템 프로그래밍 최적화를 위해, 메모리 관리나 리소스 해제등 무분별한 Copy를 막기위해 등장
struct A: Copyable {}
struct B: ~Copyable {}
우리가 배우는 소유권은 ~Copyable 상황에서 큰 힘을 발휘한다. Copyable에서는 크게 소유권이 의미가 없음
🔄 Move와 Copy, Borrow와 Consume
Borrow와 Consume은 소유권을 어떻게 다룰지를 말하고,
Copy와 Move는 값을 어떻게 전달할지를 말한다.
소유권 관점
| 의미 | 값을 잠시 빌려 사용 | 소유권을 가져감 |
| Ownership 이동 | ❌ 없음 | ✅ 있음 |
| 원래 Owner | 계속 유지 | 더 이상 사용할 수 없음 |
| 사용 목적 | 읽기, 임시 접근 | 리소스 인계, 생명주기 종료 |
| 대표 키워드 | borrowing | consuming, consume |
Value 전달 관점
| 의미 | 값을 하나 더 만든다 | 값을 새 Owner에게 넘긴다 |
| 메모리 | 새로운 값 생성 | 기존 값을 그대로 사용 |
| 원본 사용 | ✅ 가능 | ❌ 불가능 |
| Owner 수 | 2명 이상 | 항상 1명 |
| 비용 | 복사 비용 발생 가능 | 매우 저렴(Ownership만 변경) |
키워드
- borrowing (매개변수/메서드 수식어)
- 데이터를 복사하지 않고 기존 메모리 주소를 잠깐 빌려서 읽기 전용으로만 접근
함수가 끝나도 원본 데이터는 그대로 유지
- 데이터를 복사하지 않고 기존 메모리 주소를 잠깐 빌려서 읽기 전용으로만 접근
- consuming (매개변수/메서드 수식어)
- 데이터의 소유권을 함수 내부로 완전히 가져옴
- 함수 내부에서 값을 수정하거나 활용할 수 있으며, 함수가 종료되는 순간 해당 데이터는 메모리에서 해제(deinit)됨
- consume (연산자)
- 코드 실행 도중 특정 변수나 상수의 소유권을 명시적으로 종료시키거나 다른 곳으로 넘길 때 사용하는 명령
| 분류 | borrowing | consuming | consume |
| 형태 | 매개변수 / 메서드 수식어 | 매개변수 / 메서드 수식어 | 단독 연산자 (consume 변수) |
| 역할 | 읽기 전용으로 값 빌려오기 | 소유권을 가져와서 사용 후 파괴 | 변수의 소유권을 즉시 종결/이전 |
| 원본 유지 여부 | ⭕ 유지됨 | ❌ 함수 종료 후 원본 파괴 | ❌ 연산 이후 기존 변수 사용 불가 |
| 값 수정 가능 여부 | ❌ 불가 (읽기 전용) | ⭕ 가능 (소유권이 있으므로) | - (소유권 이전 목적) |
struct User: ~Copyable {
var name: String
}
// 1. borrowing 검증: 읽기만 가능하고 원본 유지
func printBorrowing(_ user: borrowing User) {
print("Borrowing: \(user.name)")
// user.name = "변경" // ❌ 컴파일 에러: borrowing 매개변수는 수정할 수 없음
}
// 2. consuming 검증: 소유권을 가져옴
func printConsuming(_ user: consuming User) {
var localUser = user // 소유권이 넘어왔으므로 로컬 변수로 변경 가능
localUser.name = "고길동"
print("Consuming: \(localUser.name)")
// 💡 함수가 끝나는 시점에 이 user 인스턴스는 메모리에서 해제됩니다.
}
// --- 검증 프로세스 ---
func testOwnership() {
let originalUser = User(name: "홍길동")
// [테스트 1] borrowing 호출
printBorrowing(originalUser)
print(originalUser.name) // ⭕ 정상 작동: originalUser가 그대로 살아있음
print("----------------")
// [테스트 2] consuming 호출
printConsuming(originalUser)
// 💡 뒤에서 originalUser를 더 이상 쓰지 않으므로, 컴파일러가 복사 없이 소유권을 넘겨버림!
// ❌ 컴파일 에러 발생!
// Error: 'originalUser' used after consume
// print(originalUser.name)
}
func testConsumeOperator() {
let targetUser = User(name: "이순신")
// [테스트 3] consume 연산자 호출
let newUser = consume targetUser // targetUser의 소유권을 newUser로 강제 이전
print(newUser.name) // ⭕ 정상 작동: 이순신
// ❌ 컴파일 에러 발생!
// Error: 'targetUser' used after consume
// print(targetUser.name)
}
아래는 consuming을 메서드로 사용했을 때와 파라미터에 명시했을 때 해석의 차이다.
- consuming func는 객체가 스스로 생명주기를 마감하는 '자폭 버튼' 같은 메서드를 만들 때 쓰고,
- consuming 파라미터는 함수가 외부 데이터를 입력받아 완전히 흡수하고 정리하는 '블랙홀' 같은 역할을 할 때 사용합니다.
// 1. 메서드에 붙었을 때: 점(.) 앞에 있는 녀석이 죽는다!
인스턴스.consuming메서드()
// 💀 점 앞에 있던 '인스턴스' 사망
// 2. 파라미터에 붙었을 때: 괄호() 안에 든 녀석이 죽는다!
일반함수(consuming파라미터)
// 💀 괄호 안에 넣었던 'consuming파라미터' 사망
struct File: ~Copyable {
consuming func close() {
// self의 소유권이 이 안으로 들어옴
print("파일을 닫고 self를 메모리에서 해제합니다.")
} // 💡 함수가 끝나는 이 시점에 self가 deinit 됨
}
let myFile = File()
myFile.close() // ⭕ 오케이!
// myFile.read() // ❌ 에러! 주체(self)인 myFile이 이미 사라짐
struct User: ~Copyable {
var name: String
}
// 파라미터에 consuming이 붙음
func processUser(_ user: consuming User) {
print("User \(user.name) 처리 중...")
} // 💡 함수가 끝나는 이 시점에 넘겨받은 user가 deinit 됨
let originalUser = User(name: "홍길동")
processUser(originalUser) // ⭕ 오케이! 소유권을 넘겨줌
// print(originalUser.name) // ❌ 에러! 외부 변수인 originalUser가 이미 사라짐
출처
반응형
'Programing Langauge > swift' 카테고리의 다른 글
| defer (0) | 2026.01.31 |
|---|---|
| 퀵 헬프 주석 (0) | 2026.01.14 |
| Transferable (0) | 2025.12.14 |
| @DynamicProperty (0) | 2025.10.24 |
| @resultBuilder (0) | 2025.10.22 |