์ผ | ์ | ํ | ์ | ๋ชฉ | ๊ธ | ํ |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- weak
- async
- ์๋ฐ
- ํ๋ก๊ทธ๋๋จธ์ค
- ์ค์ํํธ
- ํ์ด
- ์ด์ค์ผ์ดํ
- ์๋ช ์ฃผ๊ธฐ
- noncopyable
- RxSwift
- ๊ตฌ์กฐ์ฒด
- ๋ฆฌ์คํธ๋ทฐ
- ๋ฐฑ์ค
- ์๊ณ ๋ฆฌ์ฆ
- concurrency
- View
- ios
- ํ๋กํผํฐ
- ํ๋๊ทธ๋จผํธ
- rx
- ์๋๋ก์ด๋
- ์ต์ ๋
- ํด๋ก์
- Swift
- observable
- Self
- ํด์
- ์ฐ์ฐ์
- Subject
- ์ฐจ์ด
- Today
- Total
study record
[Swift] Noncopyable structs and enums ๋ณธ๋ฌธ
๐ซ SE-0390Noncopyable structs and enums
Motivation
ํ์ฌ Swift์์ ์กด์ฌํ๋ ๋ชจ๋ ํ์ ์ ๋ณต์ฌ ๊ฐ๋ฅ(copyable) ํ๋ฏ๋ก, ํด๋น ํ์ ์ ๊ฐ์ ๋ํด ์ฌ๋ฌ ๊ฐ์ ๋์ผํ๊ณ ๊ตํ ๊ฐ๋ฅํ ํํ์ ์์ฑํ ์ ์์ต๋๋ค.
ํ์ง๋ง, ๋ณต์ฌ ๊ฐ๋ฅํ ๊ตฌ์กฐ์ฒด(struct)์ ์ด๊ฑฐํ(enum)์ ๊ณ ์ ํ ์์(unique resource)์ ๋ชจ๋ธ๋งํ๋ ๋ฐ ์ ํฉํ์ง ์์ต๋๋ค.
๋ฐ๋ฉด, ํด๋์ค(class)๋ ๊ฐ์ฒด๊ฐ ํ ๋ฒ ์ด๊ธฐํ๋๋ฉด ๊ณ ์ ํ ์ ์ฒด์ฑ(unique identity)์ ๊ฐ์ง๋ฏ๋ก ๊ณ ์ ํ ์์์ ํํํ ์ ์์ต๋๋ค. ํ์ง๋ง ํด๋์ค์ ์ฐธ์กฐ(reference)๋ ์ฌ์ ํ ๋ณต์ฌ ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์, ํด๋์ค๋ ํญ์ ์์์ ๊ณต์ ์์ ๊ถ(shared ownership)์ ์๊ตฌํฉ๋๋ค.
์ด๋ฌํ ๊ณต์ ์์ ๊ถ์ ๋ค์๊ณผ ๊ฐ์ ๋ถ๊ฐ์ ์ธ ์ค๋ฒํค๋๋ฅผ ์ด๋ํฉ๋๋ค.
- ๊ฐ์ฒด์ ์ ์ฒด ์๋ช ์ด ๋ถ๋ถ๋ช ํ๊ธฐ ๋๋ฌธ์ ํ ํ ๋น(heap allocation)์ด ํ์ํจ
- ํ์ฌ ๊ฐ์ฒด๋ฅผ ๊ณต์ ํ๋ ์์ ์์ ์๋ฅผ ์ถ์ ํ๊ธฐ ์ํด ์ฐธ์กฐ ์นด์ดํ (reference counting)์ด ํ์ํจ
๋ํ, ๊ณต์ ์ ๊ทผ ๋ฐฉ์์ ๊ฐ์ฒด์ API๋ฅผ ๋ณต์กํ๊ฒ ๋ง๋ค๊ฑฐ๋, ์์ ์ฑ ๋ฌธ์ ๋ฅผ ์ด๋ํ๊ฑฐ๋, ์ถ๊ฐ์ ์ธ ์ค๋ฒํค๋๋ฅผ ๋ฐ์์ํค๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค.
ํ์ฌ Swift์๋ ๊ณ ์ ํ ์์ ๊ถ(unique ownership)์ ๊ฐ์ง๋ ๊ณ ์ ํ ์์์ ํํํ๋ ํ์ ์ ์ ์ํ๋ ๋ฉ์ปค๋์ฆ์ด ์กด์ฌํ์ง ์์ต๋๋ค.
Proposed Solution
์ฐ๋ฆฌ๋ ๊ตฌ์กฐ์ฒด(struct)์ ์ด๊ฑฐํ(enum) ํ์ ์ด noncopyable ๊ฐ๋ฅํ๋๋ก ์ ์ธํ ์ ์๋๋ก ์ ์ํ๋ฉฐ, ์ด๋ฅผ ์ํด ์๋ก์ด ๋ฌธ๋ฒ ~Copyable์ ์ฌ์ฉํ์ฌ ์์์ ์ธ ์ ๋ค๋ฆญ ์ ์ฝ์ ์ต์ (suppress)ํ๋ ๋ฐฉ์์ ๋์ ํ๋ ค ํฉ๋๋ค.
~Copyable์ ์ฌ์ฉํ๋ฉด Swift๊ฐ ์ ๋ค๋ฆญ ํ์ ์ด ์๋์ผ๋ก Copyable์ ๋ฐ๋ฅด๋๋ก ํ๋ ์์์ ์ธ ์ ์ฝ์ ์ ๊ฑฐํ ์ ์๋ค.
noncopyable ํ์ ์ ๊ฐ์ ํญ์ ๊ณ ์ ํ ์์ ๊ถ(unique ownership)์ ๊ฐ์ง๋ฉฐ, ์ ๋๋ก ๋ณต์ฌ๋ ์ ์์ต๋๋ค(์ ์ด๋ Swift์ ์์์ ๋ณต์ฌ ๋ฉ์ปค๋์ฆ์ ํตํด์๋ ๋ถ๊ฐ๋ฅํฉ๋๋ค).
noncopyable ๊ตฌ์กฐ์ฒด์ ์ด๊ฑฐํ์ ๊ฐ์ ๊ณ ์ ํ ์ ์ฒด์ฑ(unique identity)์ ๊ฐ์ง๋ฏ๋ก, ํด๋์ค์ฒ๋ผ deinit ์ ์ธ์ ๊ฐ์ง ์ ์์ต๋๋ค. ์ด๋ ํด๋น ๊ณ ์ ์ธ์คํด์ค์ ์๋ช ์ฃผ๊ธฐ๊ฐ ๋๋ ๋ ์๋์ผ๋ก ์คํ๋ฉ๋๋ค.
struct FileDescriptor: **~Copyable** {
private var fd: Int32
init(fd: Int32) { self.fd = fd }
func write(buffer: Data) {
buffer.withUnsafeBytes {
write(fd, $0.baseAddress!, $0.count)
}
}
**deinit** {
close(fd)
}
}
NonCopyable ํ์ ์ผ๋ก ์ ์ธ๋์๊ธฐ ๋๋ฌธ์,
- ๊ฐ์ ๋ณต์ฌํ ์ ์์ (์ฆ, ๋ ๊ฐ ์ด์์ ๋์ผํ FileDescriptor ์ธ์คํด์ค๋ฅผ ๋ง๋ค ์ ์์)
- ๊ฐ์ ํญ์ ๊ณ ์ ํ ์์ ๊ถ(unique ownership)์ ๊ฐ์ง
- ๊ฐ์ด ๋ณต์ฌ๋์ง ์์ผ๋ฏ๋ก ๊ฐ ์ธ์คํด์ค๊ฐ ์ ์ผํ ํ์ผ ํธ๋ค์ ๊ด๋ฆฌ
- ๋ถํ์ํ ์ฐธ์กฐ ์นด์ดํ (reference counting)๊ณผ ๊ณต์ ๋น์ฉ์ด ์์ ⇒ ๋น ๋ฅด๊ณ ์์ ํ ์ฑ๋ฅ
์ด์ Swift์์ ~Copyable์ ํ์ฉํ๋ฉด ๊ตฌ์กฐ์ฒด(struct)๋ฅผ ํตํด ๊ณ ์ ํ ๋ฆฌ์์ค๋ฅผ ์์ ํ๊ณ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ ์ ์์ต๋๋ค.
- ๊ธฐ์กด์๋ ํด๋์ค๋ฅผ ์ฌ์ฉํด์ผ ํ์ง๋ง, ์ด์ ๋ Heap ํ ๋น ์์ด, ๋ ์์ ํ๊ณ ๋น ๋ฅด๊ฒ ๊ด๋ฆฌ ๊ฐ๋ฅ
- deinit ์ง์์ ํตํด ์๋ ๋ฆฌ์์ค ํด์ ๊ฐ๋ฅ
- ๋ถํ์ํ ๋ณต์ฌ ๋ฐฉ์ง๋ก ์ฑ๋ฅ ์ต์ ํ
๋ฐ๋ผ์, ~Copyable์ ํ์ผ ๋์คํฌ๋ฆฝํฐ, ๋คํธ์ํฌ ์์ผ, GPU ๋ฒํผ ๊ฐ์ ๊ณ ์ ํ ์์(unique resource)์ ๊ด๋ฆฌํ๋ ๋ฐ ๋งค์ฐ ์ ํฉํ ๊ธฐ๋ฅ์ ๋๋ค.
Noncopyable ํ์ ์ ํฌํจํ๋ ๊ฒฝ์ฐ
- ๊ตฌ์กฐ์ฒด(struct)๊ฐ ๋น๋ณต์ฌ ํ์ ์ ์ ์ฅ ํ๋กํผํฐ(stored property)๋ก ํฌํจํ๊ฑฐ๋,
- ์ด๊ฑฐํ(enum)์ด ๋น๋ณต์ฌ ํ์ ์ ์ฐ๊ด ๊ฐ(associated value)์ผ๋ก ํฌํจํ๋ ๊ฒฝ์ฐ,
→ ํด๋น ๊ตฌ์กฐ์ฒด ๋๋ ์ด๊ฑฐํ๋ ๋ฐ๋์ ~Copyable์ ์ ์ธํด์ผ ํฉ๋๋ค.
์ฆ, noncopyable ํ์ ์ ํฌํจํ๋ ํ์ ์ญ์ ์๋์ผ๋ก noncopyable ํ์ ์ด ๋์ด์ผ ํฉ๋๋ค.
struct SocketPair: ~Copyable {
var in, out: FileDescriptor
}
enum FileOrMemory: ~Copyable {
// write to an OS file
case file(FileDescriptor)
// write to an array in memory
case memory([UInt8])
}
// **ERROR**: **copyable value type cannot contain noncopyable members**
struct FileWithPath {
var file: FileDescriptor
var path: String
}
Class์ noncopyable
Class์ ๊ฒฝ์ฐ์๋ ~Copyable์ ์ฑํํ์ง ์์๋ noncopyable ํ๋กํผํฐ๋ฅผ ๊ฐ์ง ์ ์๋ค.
class SharedFile {
var file: FileDescriptor
}
ํด๋์ค(class) ํ์ ์ ์ธ์ ~Copyable์ ์ฌ์ฉํ ์ ์์
๋ชจ๋ ํด๋์ค ํ์ ์ ์ฌ์ ํ ๋ณต์ฌ ๊ฐ๋ฅ(copyable) ํ๋ฉฐ, ~Copyable์ ์ ์ธํ ์ ์์ต๋๋ค.
์ด๋ ํด๋์ค๊ฐ ๊ฐ์ฒด์ ๋ํ ์ฐธ์กฐ(reference)๋ฅผ ๋ณต์ฌํ ๋, ์ฐธ์กฐ ์นด์ดํ (retain/release)์ ํตํด ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๊ด๋ฆฌํ๋ ๋ฐฉ์ ๋๋ฌธ์ ๋๋ค. ์ฆ, ํด๋์ค ์ธ์คํด์ค ์์ฒด๊ฐ ๋ณต์ฌ๋๋ ๊ฒ์ด ์๋๋ผ ๊ฐ์ฒด์ ์ฐธ์กฐ๋ง ๋ณต์ฌ๋๋ฏ๋ก, ~Copyable์ ์ ์ฉํ ํ์๊ฐ ์์ต๋๋ค.
โ ์ฆ, ํด๋์ค๋ ํญ์ ์ฐธ์กฐ ํ์ (reference type)์ด๋ฏ๋ก ๋ณต์ฌ ๊ฐ๋ฅํ๋ฉฐ, ~Copyable์ ์ ์ฉํ ์ ์์ต๋๋ค.
// **ERROR: classes must be `Copyable`**
class SharedFile: ~Copyable {
var file: FileDescriptor
}
Noncopyable ํ์ ์ด ์ ๋ค๋ฆญ ํ์ ์ธ์๋ก ์ฌ์ฉ๋ ์ ์์
ํ์ฌ Swift์ ์ ๋ค๋ฆญ ์์คํ ์ ์ ๋ค๋ฆญ ํ์ ์ด ํญ์ Copyableํ ๊ฒ์ ์๊ตฌํฉ๋๋ค.
์ฆ, noncopyable ํ์ (~Copyable)์ ์ ๋ค๋ฆญ ์ธ์๋ก ์ ๋ฌํ๋ ๊ฒ์ ๋ถ๊ฐ๋ฅํฉ๋๋ค.
์ด๋ก ์ธํด noncopyable ํ์ (~Copyable)์ด ํ ์ ์๋ ๊ฒ๋ค:
- ํ๋กํ ์ฝ ์ค์ ๋ถ๊ฐ (๋จ, Sendable์ ์์ธ)โ FileDescriptor๋ ๋น๋ณต์ฌ ํ์ ์ด๋ฏ๋ก CustomStringConvertible ๊ฐ์ ํ๋กํ ์ฝ์ ๋ฐ๋ฅผ ์ ์์.
- โ ๋จ, Sendable ํ๋กํ ์ฝ์ ์์ธ์ ์ผ๋ก ๊ตฌํ ๊ฐ๋ฅ.
- struct FileDescriptor: ~Copyable, CustomStringConvertible { ... } // โ ๋ถ๊ฐ๋ฅ!
- ์ ๋ค๋ฆญ ํจ์์ ํ์ ์ธ์๋ก ์ฌ์ฉ ๋ถ๊ฐ
func process<T>(_ value: T) { ... }
let fd = FileDescriptor(fd: 10)
process(fd) // โ ๋ถ๊ฐ๋ฅ!
- Any ๋๋ ๋ค๋ฅธ ์กด์ฌ ํ์ (existential)์ผ๋ก ์บ์คํ ๋ถ๊ฐ
let anyValue: Any = FileDescriptor(fd: 10) // โ ๋ถ๊ฐ๋ฅ!
๋น๋ณต์ฌ ํ์ ์ Any๋ ๋ค๋ฅธ ์กด์ฌ ํ์ ์ผ๋ก ๋ณํํ ์ ์์.
- โ ๋น๋ณต์ฌ ํ์ (~Copyable)์ ์์ฒด์ ์ผ๋ก๋ง ์ฌ์ฉ ๊ฐ๋ฅํ๋ฉฐ,
- โ ์ ๋ค๋ฆญ ์ธ์๋ก ์ ๋ฌํ๊ฑฐ๋, ํ๋กํ ์ฝ์ ๋ฐ๋ฅด๊ฑฐ๋, Any๋ก ๋ณํํ๋ ๋ฑ ๋ณต์ฌ๊ฐ ๊ฐ๋ฅํ ์ปจํ ์คํธ์์๋ ์ฌ์ฉ ๋ถ๊ฐ๋ฅ
→
์ฆ, ~Copyableํ ๊ตฌ์กฐ์ฒด ๋๋ ์ด๊ฑฐํ์ ์ค์ง ์๊ธฐ ์์ ๋ง ํ์ ํ์ (subtype)์ผ๋ก ๊ฐ์ง ์ ์์
๋ค๋ฅธ ํ์ ๊ณผ์ ๋ณํ์ด ๋ถ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์ ๋ณต์ฌ๋ฅผ ํ์ฉํ๋ ์ปจํ ์คํธ์์ ์ฌ์ฉ๋ ์ ์์
noncopyable struct ์ธ์คํด์ค๋ฅผ ํ ๋นํ๋ ๊ฒ ์์ฒด๊ฐ consume
let x = FileDescriptor()
let y = x
use(x) // ERROR: x consumed by assignment to `y`
noncopyable types์ ํ๋ผ๋ฏธํฐ๋ก ์ฌ์ฉ ์, borrowing, consuming, or inout convention ์ฌ์ฉ ํ์
// Redirect a file descriptor
// Require exclusive access to the FileDescriptor to replace it
func redirect(_ file: inout FileDescriptor, to otherFile: borrowing FileDescriptor) {
dup2(otherFile.fd, file.fd)
}
// **Error: Noncopyable parameter must specify its ownership**
func redirect(_ file: FileDescriptor, to otherFile: borrowing FileDescriptor) {
dup2(otherFile.fd, file.fd)
}
// Write to a file descriptor
// Only needs shared access
func write(_ data: [UInt8], to file: borrowing FileDescriptor) {
data.withUnsafeBytes {
write(file.fd, $0.baseAddress, $0.count)
}
}
// Close a file descriptor
// Consumes the file descriptor
func close(file: consuming FileDescriptor) {
close(file.fd)
}
ํจ์๋ ๋ง์ฐฌ๊ฐ์ง
extension FileDescriptor {
mutating func replace(with otherFile: borrowing FileDescriptor) {
dup2(otherFile.fd, self.fd)
}
// borrowing by default
func write(_ data: [UInt8]) {
data.withUnsafeBytes {
write(file.fd, $0.baseAddress, $0.count)
}
}
consuming func close() {
close(fd)
}
}
Noncopyable ํ์ ์ ํ๋กํผํฐ ์ ์ธ
1. Noncopyable ํ์ ์ ์ ์ฅ ํ๋กํผํฐ๋ก ์ ์ธ
ํด๋์ค ๋๋ ~Copyable struct๋ Noncopyable ํ์ ์ ์ ์ฅ ํ๋กํผํฐ(let ๋๋ var)๋ก ์ ์ธํ ์ ์๋ค.
let ์ ์ฅ ํ๋กํผํฐ
- let ํ๋กํผํฐ๋ ์ค์ง borrow๋ง ๊ฐ๋ฅ
- ์ฆ, ์ฝ์ ์๋ ์์ง๋ง ๋ณ๊ฒฝํ๊ฑฐ๋ consumeํ ์ ์์
var ์ ์ฅ ํ๋กํผํฐ
- var ํ๋กํผํฐ๋ borrow์ mutate๋ชจ๋ ๊ฐ๋ฅ
- ํ์ง๋ง ์ผ๋ฐ์ ์ผ๋ก consume์ ๋ถ๊ฐ๋ฅ (์ ํ์ ์ผ๋ก ๊ฐ๋ฅ)
- ์ด์ : ํ๋กํผํฐ๊ฐ ์๋น๋๋ฉด ํด๋น ์ธ์คํด์ค(๊ตฌ์กฐ์ฒด/ํด๋์ค)๊ฐ ๋ฌดํจ ์ํ๊ฐ ๋ ์ ์๊ธฐ ๋๋ฌธ
struct NonCopyableResource: ~Copyable {
var id: Int
}
struct Container: ~Copyable {
let resource: NonCopyableResource // โ
let ํ๋กํผํฐ (borrow๋ง ๊ฐ๋ฅ)
func consumeResource() {
let resourceCopy = move(resource) // โ ERROR: 'resource' cannot be consumed because it is immutable
}
func move(_ resource: consuming NonCopyableResource) {
consume resource
}
}
struct Container: ~Copyable {
var resource: NonCopyableResource // โ
var ํ๋กํผํฐ (borrow + mutate ๊ฐ๋ฅ)
mutating func updateResource(id: Int) {
resource.id = id // โ
mutate ๊ฐ๋ฅ (์์ ๊ฐ๋ฅ)
}
func consumeResource() {
let resourceCopy = move(resource) // โ ERROR: 'resource' cannot be consumed
}
func move(_ resource: consuming NonCopyableResource) {
consume resource
}
}
2. Noncopyable ํ์ ์ computed ํ๋กํผํฐ๋ก ์ ์ธ
- ๋ชจ๋ ํ์ ์ Noncopyable ํ์ ์ ๊ณ์ฐ ํ๋กํผํฐ๋ก ์ ์ธ ๊ฐ๋ฅ
- get ์ ๊ทผ์๋ ์์ ๊ถ(owned value)์ ๋ฐํํ ์ ์์ → ์ฆ, ํธ์ถํ ์ชฝ์์ ๊ฐ์ consumeํ ์ ์์
- set ์ ๊ทผ์๋ newValue๋ฅผ consumeํ๋ ๋ฐฉ์์ผ๋ก ๋ฐ์ → ์ฆ, ๊ธฐ์กด ๊ฐ์ consumeํ์ฌ ์๋ก์ด ๊ฐ์ผ๋ก ๊ต์ฒด ๊ฐ๋ฅ
consuming get์ ํ์ฉํ ์์ ๊ถ ์ด์
- consuming get์ ์ฌ์ฉํ๋ฉด ์ผ๋ถ ๊ฐ์ ์์ ๊ถ๊ณผ ํจ๊ป ๋ฐํํ ์ ์์
- ์ด๋ ๋ํผ(wrapper) ํ์ ์์ ๋ด๋ถ ๊ฐ์ ์์ ๊ถ์ ๋๊ฒจ์ฃผ๋ ๋ฐฉ์์ผ๋ก ์ ์ฉ
struct FileDescriptorWrapper: ~Copyable {
private var _value: FileDescriptor
var value: FileDescriptor { // 'self' is borrowed and cannot be consumed ?!
consuming get { return _value }
}
}
Class์์ Noncopyable ์ ์ฅ ํ๋กํผํฐ ์ฌ์ฉ
1) ํด๋์ค์ ์ ์ฅ ํ๋กํผํฐ์ ๋์ ๋ฐฐํ์ฑ ๊ฒ์ฌ(Dynamic Exclusivity Checking)
Swift์์๋ ๊ฐ์ฒด(Class Instance)๊ฐ ์ฌ๋ฌ ์ฐธ์กฐ(reference)๋ฅผ ๊ฐ์ง ์ ์๊ธฐ ๋๋ฌธ์,
๋์์ ๊ฐ์ ์ ์ฅ ํ๋กํผํฐ๋ฅผ ์์ ํ๋ ๊ฒ์ ๋ฐฉ์งํ๊ธฐ ์ํด "๋์ ๋ฐฐํ์ฑ ๊ฒ์ฌ(Dynamic Exclusivity Checking)"๋ฅผ ์ํํฉ๋๋ค.
์ด ๋์ ๊ฒ์ฌ(runtime check)๋ ~Copyable ์ ์ฅ ํ๋กํผํฐ์๋ ์ ์ฉ๋ฉ๋๋ค.
์ฆ, ๋์์ borrow(๋์ฌ)์ mutate(๋ณ๊ฒฝ)๊ฐ ์๋๋๋ฉด, ๋ฐํ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค.
Swift๋ borrow ์ํ์์ mutate๊ฐ ๋ถ๊ฐ๋ฅํ๋๋ก ๊ฐ์ ํจ
class Foo {
var fd: FileDescriptor
init(fd: FileDescriptor) { self.fd = fd }
}
func update(_: inout FileDescriptor, butBorrow _: borrow FileDescriptor) {}
func updateFoo(_ a: Foo, butBorrowFoo b: Foo) {
update(&a.fd, butBorrow: b.fd)
}
let foo = Foo(fd: FileDescriptor())
// Will trap at runtime when foo.fd is borrowed and mutated at the same time
updateFoo(foo, butBorrowFoo: foo)
Noncopyable ๋ณ์๊ฐ escaping ํด๋ก์ ์์ ์บก์ฒ๋ ๋์ ๋์
nonescaping vs. escaping ํด๋ก์ ์์ Noncopyable ๋ณ์ ์บก์ฒ ์ฐจ์ด
- nonescaping ํด๋ก์ ๋ ์ ํจ ๋ฒ์(scope)๊ฐ ํ์ ๋จ → ๋ฐ๋ผ์ ๋ณ์๋ฅผ borrow(๋์ฌ)ํ ์ ์์
- escaping ํด๋ก์ ๋ ํ๋ก๊ทธ๋จ์ด ์คํ๋๋ ๋์ ์ธ์ ๋ ์ง ํธ์ถ๋ ์ ์์ → ๋ฐ๋ผ์ ๋ณ์์ ์์ ๊ถ์ ๋ช ํํ๊ฒ ๊ด๋ฆฌํด์ผ ํจ
๐ก ์ฆ, escaping ํด๋ก์ ์์ Noncopyable ๋ณ์๋ฅผ ์บก์ฒํ๋ฉด, ๋ณ์๋ ํญ์ borrow ์ํ๊ฐ ๋๋ฉฐ consume(์๋น)ํ ์ ์์
- escaping ํด๋ก์ ๋ ํ์ฌ ์ค์ฝํ๋ฅผ ๋ฒ์ด๋์๋ ์คํ๋ ์ ์์
- ์ผ๋ฐ์ ์ผ๋ก Swift์ Copyableํ ๊ฐ๋ค์ escaping ํด๋ก์ ์์ ์์ ํ๊ฒ ๋ณต์ฌ ๊ฐ๋ฅ
- ํ์ง๋ง ๋น๋ณต์ฌ(~Copyable) ๋ณ์๋ ๋ณต์ฌํ ์ ์์ผ๋ฏ๋ก, consumeํ๋ฉด ์๋ณธ์ด ์ฌ๋ผ์ง
๐จ ์ฆ, consumeํ๋ฉด ๋ณ์๊ฐ ๋ ์ด์ ์กด์ฌํ์ง ์์ผ๋ฏ๋ก, ์ดํ ์คํ๋ ํด๋ก์ ์์ ํด๋น ๋ณ์๋ฅผ ์ฌ์ฉํ ์ ์์
import Dispatch
struct FileDescriptor: ~Copyable {
private var fd: Int32
init(fd: Int32) { self.fd = fd }
func read() {
print("Reading from fd: \\(fd)")
}
consuming func close() {
print("Closing fd: \\(fd)")
}
}
func escape(_ closure: @escaping () -> Void) {
DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
closure()
}
}
func test() {
let file = FileDescriptor(fd: 42)
escape {
file.read() // โ
`borrow` ๊ฐ๋ฅ (์ฝ๊ธฐ ์ ์ฉ)
// file.close() // โ ERROR: `file`์ `escaping` ํด๋ก์ ์์ `borrow` ์ํ์ด๋ฏ๋ก `consume` ๋ถ๊ฐ
}
}
test()
๊ตฌ์กฐ์ฒด(struct) ๋ฐ ์ด๊ฑฐํ(enum)์ deinit ํน์ง
- ํด๋์ค์ deinit๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก, struct ๋ฐ enum์ deinit์์๋ ์ค๋ฅ๋ฅผ ๋์ง ์ ์์
- deinit ๋ด๋ถ์์ self๋ borrow ์ํ
- ์ฆ, mutate(๋ณ๊ฒฝ)ํ๊ฑฐ๋ consume(์๋น)ํ ์ ์์
struct FileDescriptor: ~Copyable {
private var fd: Int32
init(fd: Int32) { self.fd = fd }
deinit {
print("Closing file descriptor \\(fd)")
// self.fd = -1 โ ERROR: `deinit` ๋ด๋ถ์์๋ `mutate` ๋ถ๊ฐ
// consume self โ ERROR: `deinit` ๋ด๋ถ์์๋ `consume` ๋ถ๊ฐ
}
}
๋ฐ๊นฅ ~Copyable์ด ๋จผ์ deinit ๋จ.
struct Inner: ~Copyable {
deinit { print("destroying inner") }
}
struct Outer: ~Copyable {
var inner = Inner()
deinit { print("destroying outer") }
}
do {
_ = Outer()
}
// destroying outer
// destroying inner
discard self๋ฅผ ํ์ฉํ์ฌ deinit ์คํ ์ต์
Noncopyable ํ์ ์์ deinit์ด ์คํ๋์ง ์๋๋ก ํ๊ธฐ ์ํด discard self ์ฐ์ฐ์๋ฅผ ๋์
- discard self๋ ํด๋น ๊ฐ์ฒด(self)์ ์๋ช ์ ์ข ๋ฃํ์ง๋ง, deinit์ ํธ์ถํ์ง ์์
- ์ด๋ฅผ ํตํด ๋ฆฌ์์ค๋ฅผ ์ง์ ์ ์ด ๊ฐ๋ฅ
struct FileDescriptor: ~Copyable {
private var fd: Int32
deinit {
close(fd)
}
consuming func close() {
close(fd)
// The lifetime of `self` ends here, triggering `deinit` (and another call to `close`)!
}
}
struct FileDescriptor: ~Copyable {
private var fd: Int32
// ํ์ผ ๋์คํฌ๋ฆฝํฐ๋ฅผ ๋ซ์ ๋, `deinit`์ด ์คํ๋์ง ์๋๋ก ์ต์
consuming func close() throws {
if close(fd) != 0 {
throw CloseError(errno)
}
discard self // โ
`deinit`์ด ์คํ๋์ง ์์
}
}
์กฐ๊ฑด๋ถ๋ก deinit์ ์คํํ ์ง ์ฌ๋ถ๋ฅผ ๊ฒฐ์ ๊ฐ๋ฅ
โ discard self๋ฅผ ์ฌ์ฉํ๋ฉด deinit์ด ์คํ๋์ง ์๊ณ , consume self๋ฅผ ์ฌ์ฉํ๋ฉด deinit ์คํ๋จ
struct MemoryBuffer: ~Copyable {
private var address: UnsafeRawPointer
init(size: Int) throws {
guard let address = malloc(size) else {
throw MallocError()
}
self.address = address
}
deinit {
free(address)
}
consuming func takeOwnership(if condition: Bool) -> UnsafeRawPointer? {
if condition {
// Save the memory buffer and give it to the caller, who
// is promising to free it when they're done.
let address = self.address
**discard self**
return address
} else {
// We still want to free the memory if we aren't giving it away.
**_ = consume self**
return nil
}
}
}
'Swift' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Swift] consume, consuming, borrowing (0) | 2025.03.03 |
---|