iOS/Common

[Swift] ~Copyable

TDCIAN 2025. 7. 19. 11:25

 

 

Swift의 ~Copyable

Swift 6(및 Swift 5.9)부터 새롭게 등장한 ~Copyable 키워드는, Swift 개발자들에게 "값 타입도 이제 move-only(복사 금지, 이동 전용)로 만들 수 있다"는 혁신을 가져왔습니다.

 

1. ~Copyable, 왜 등장했을까?

기존 Swift는 struct와 enum의 값(인스턴스)을 아무 제약 없이 자유롭게 복사할 수 있었습니다. 대부분은 큰 문제가 없지만, 고유한 리소스(예: 파일 디스크립터, 보안 키 등)처럼 '정확히 하나'만 존재해야 하는 값 타입(struct, enum)으로 안전하게 관리하기란 어려웠죠.

 

Swift 6 이전에는 이런 리소스를 관리하려면 class와 deinit을 써야 했습니다. 하지만 class는 참조 카운팅에 의존하며, 값 타입의 효율이나 안전성(특히 스레드 안정성)을 온전히 얻지 못했습니다.

 

~Copyable은 struct와 enum에서 유니크한 리소스의 소유권을 코딩으로 명확하게 관리하자라는 취지에서 도입된 키워드입니다.

 

 

2. ~Copyable의 기본 개념

- ~Copyable은 "이 타입은 복사하지 마!"라는 약속입니다.

  - 즉, 복사 없이 "이동(move)"만 가능합니다.

- move-only 타입으로 선언하면, 값을 변수에 대입하거나 함수에 넘기는 순간 소유권(ownership)이 통째로 넘어가고, 이전 변수는 더 이상 쓸 수 없습니다.

 

예) 은행 OTP 토큰처럼 하나만 존재해야 하고 복사되면 위험한 값

struct OneTimeToken: ~Copyable {
	let value: String
    
    consuming func use() {
    	print("토큰 사용: \(value)")
        // 여기서 self는 완전히 소모됨
    }
}

var token = OneTimeToken(Value: "SECRET")
token.use()
// token.use() // 컴파일 에러 발생함 -> token은 이미 소모 됐으므로

 

 

3. ~Copyable의 문법과 사용법

 

1) 선언 방법

struct MyResource: ~Copyable {
	// ...
}

enum MyOption: ~Copyable {
	// ...
}

 

struct, enum에만 사용 가능(class는 지원 X, class는 deinit으로 메모리/리소스 관리)


2) 소유권 이동 및 소비

- "이동(move)"하면 이전 변수는 더 이상 사용 불가

var a = MyResource()
var b = a // 소유권이 a에서 b로 이동
// print(a) // 에러 발생, a는 이미 이동 됐으므로

- 함수 인자로 사용할 때: 명확한 소유권 선언 필요

  - consuming 파라미터: 소유권 가져가서 이전 변수는 못 씀

  - inout: 소유권을 잠시 빌려썼다가 돌려줌

  - borrowing: 값을 잠깐 빌려쓰지만 소유권 이동 X (읽기 전용)

func process(consuming resource: MyResource) {
	// resource 사용하고 나서 resource 자체도 소모됨
}

var r = MyResource()
process(consuming: r) // r은 여기서 소모되어 이후 사용 불가

 

 

 

4. ~Copyable은 어떤 때 유용할까?

- 파일 디스크립터, 시스템 핸들 같은 고유 리소스를 값 타입으로 안전하게 관리하고 싶을 때

- 암호화 키 등 복사 금지가 중요한 데이터를 구조체로 만들고 싶을 때

- 데이터 경쟁이나 중복 없이, 구조체의 효율과 Swift의 안정성을 최대한 살릴 때

 

 

5. 실전 예제: FileDescriptor

struct FileDescriptor: ~Copyable {
	private var fd: Int32
    
    init(fd: Int32) {
    	self.fd = fd
    }
    
    func write(data: Data) {
    	data.withUnsafeBytes { buffer in
			_ = Darwin.write(fd, buffer.baseAddress!, buffer.count)        	
        }
    }
    
    deinit {
    	close(fd) // 소유권이 이동된 곳에서만 close 호출
    }
}

func processFile(consuming fd: FileDescriptor) {
	// fd를 안전하게 사용(소비 후 fd는 사라짐)
}

이렇게 하면 fd가 여러 군데서 close 되는 버그나, 시스템 리소스 누수 없이 오직 하나만 안전하게 관리됩니다.

 

 

6. ~Copyable 요약

특징 설명
대상 struct, enum(class는 X)
주요 효과 값 복사 금지, 이동만 가능(move-only)
도입 목적 유니크 리소스 소유권, 값 타입 효율, 안정성
실제 사용 예 파일 디스크립터, 보안 키, 시스템 핸들 등

 

7. 참고하기 좋은 영상

https://developer.apple.com/videos/play/wwdc2024/10170/

 

Consume noncopyable types in Swift - WWDC24 - Videos - Apple Developer

Get started with noncopyable types in Swift. Discover what copying means in Swift, when you might want to use a noncopyable type, and how...

developer.apple.com

 

 

도움: Perflexity