원문 캡처 · denismarkelov.codeberg.page
원문 캡처 · denismarkelov.codeberg.page

메모리 관리

메모리 관리에서는 타입 이름에 @ref를 붙이면 구조체가 힙에 초기화된다. vector 바인딩의 타입은 mut ref Vector3이며, MutRef<Vector3>로도 쓸 수 있다. 이런 참조는 포인터 연산을 지원하지 않는 포인터와 본질적으로 같다.

let vector @owner = Vector3@ref 1 3 8

@owner 속성은 바인딩의 범위가 끝나기 전에 메모리를 해제한다. @[owner function]은 바인딩을 포함하는 함수가 반환되기 전에 메모리를 해제한다.

객체

객체에는 기본적으로 자동 참조 카운팅(reference counting)이 적용된다.

type App =
 val name : String

let app = App "Editor"

@mrc 한정자를 사용하면 객체가 수동 참조 카운팅으로 전환된다. 참조 카운트는 kd/retain과 kd/release 함수로 관리한다.

let app = App@mrc "Editor"

kd/retain app
kd/release app

@ref를 붙이면 app은 힙에 있는 객체를 참조한다. 이 객체는 kd/free 함수로 직접 해제해야 한다.

let app = App@ref "Editor"

kd/free app

app 바인딩의 타입이 mem App이면 객체의 메모리를 담는다. app에 접근하면 App 타입의 참조를 반환한다. 복사하려면 @copy 한정자를 붙여야 한다.

let app = App@mem "Editor"
let app_ref = app
let app_copy = app@copy

create 함수가 mem App을 반환하더라도, app 바인딩은 힙에 할당된 참조 카운팅 방식의 App 객체를 가리킨다.

def create = App@mem "Editor"

let app = create@arc

@mem을 기본 옵션으로 지정하면 App에서 참조 카운팅을 사용할 수 없다. 다만 @ref는 계속 사용할 수 있다.

type App @mem =
 val name : String

let app = App@ref "Editor"

복사·이동 의미론을 위한 통합 코드

복사·이동 의미론을 위한 통합 코드에서는 List<mem T>를 다음과 같이 정의한다.

type List<mem T> =
 var size : u32 = 0
 let mut array = Array<mem T> 0

 fun get (i : u32) = array[i]

 def index_of (x : T) =
 for i = 0 until size do
 if array[i] == x then return i

 fail "no such item"

 def add (x : mem T) =
 if size == array.size then
 set_array_size (if size == 0 then 4 else size * 2)

 kd/retain x
 array[size] = x@move
 size += 1

 def remove_at (index : u32) =
 kd/clear array[index]

 if size > 1 then
 for i = index to size - 2 do
 array[i] = array[i + 1]@move

 size -= 1

 def remove (x : T) =
 let index = index_of x
 remove_at index

mem T는 참조 타입에서 객체의 메모리를 나타낸다. 값 타입에서는 타입 자체와 같아 mem T == T가 되며, 이때 @copy와 @move 연산은 아무 작업도 하지 않는다.

kd/clear 함수는 mem 타입을 파괴하고 참조 카운팅 객체의 참조 카운트를 줄인다.