본문 바로가기
iOS와 그 외/Swift

[Swift] inout 매개변수

by lody.park 2024. 7. 19.

안녕하세요 로디입니다. 

오늘은 Swift의 inout 키워드에 대해서 간단하게 정리하겠습니다.

 

Swift에서, 함수의 매개변수(parameter)로 전달되는 "값 타입"은 기본적으로 상수(let)입니다.

* 값 타입
Swift의 값 타입은 구조체(struct)를 사용하여 표시합니다. 예를 들어 String, Int, Array, Dictionary 등.

 

그렇기에, 함수의 body 내부에서 매개변수로 전달받은 값을 바꾸는 것은 불가능합니다.

아래와 같이 매개변수 값을 바꾸려고하면 컴파일러가 에러를 표시하죠.

 

그렇다면 함수 내부에서 매개변수의 값을 바꾸는 게 영영 불가능할까요?

아뇨, 매개변수를 inout 키워드를 이용해 inout 매개변수로 정의하면 가능합니다.

 

Swift의 인아웃 매개변수(In-Out Parameter) 정의하기

매개 변수를 In-Out 매개변수로 표시하려면 아래와 같이 매개변수의 타입 앞에 Inout 키워드를 적어줍니다. 

func incrementByOne(number: inout Int) {
    number = number + 1
}

 

inout 매개변수에 값을 전달하는 방법

inout 매개변수에 전달인자를 전달할 때는 변수 이름 앞에 앰퍼샌드(&)를 직접 배치해야 합니다. 이는 호출 시점에서 매개변수가 inout 매개변수임을 명확하게 하여 원래 값이 함수 호출 후 변경될 수 있음을 알리기 위함입니다.

var count = 0
        
func incrementByOne(number: inout Int) {
    number = number + 1
}
// 1
incrementByOne(number: &count)

print(count)
// print 1

 

위 예제에서 볼 수 있듯이, inout 매개변수 number를 수정하여 count 변수의 값을 0에서 1로 변경할 수 있습니다. 만약 &를 붙이는 것을 잊어버리면 컴파일러가 다음과 같은 경고를 표시합니다:

* call cite
함수를 호출(또는 동적 디스패치를 ​​통해 호출 할 수 있음)하는 위치 (코드 라인)입니다.
call cite 는 0개 이상의 인자들이 함수로 전달되고, 0개 이상의 return 값을 받는 곳입니다.

즉 아래와 같은 위치를 말합니다.
let incrementResult = incrementByOne(number: &0)

 

inout 매개변수는 참조에 의한 전달과 동일할까?

Swift의 inout 매개변수가 내부적으로 어떻게 동작하는지 궁금했습니다.

처음에는 참조에 의한 전달과 유사할 것이라고 생각했지만, inout 매개변수의 동작은 그것보다 단순합니다.

 

1. 함수가 호출되면 전달인자의 값이 복사됩니다.

2. 함수 본문 내에서 복사된 값을 수정합니다.

3. 함수가 반환될 때 복사된 값이 원래 전달인자에 다시 할당됩니다.

 

이 동작은 copy-in copy-out 또는 call by value result로 알려져 있습니다.

Swift는 함수 본문 내부와 외부 모두에서 동일한 메모리 위치를 사용하여 copy-in copy-out 동작을 최적화합니다. 이는 참조에 의한 호출(call by reference)과 동일한 원리입니다.

그러나 이러한 최적화 세부 사항은 미래에 변경될 수 있으므로 기본적인 copy-in copy-out 모델에 기반하여 코드를 작성해야 합니다. 즉, 내부 구현 세부 사항에 의존해서는 안 됩니다.

 

 

inout 매개변수를 사용해야 할까?

 

불변 또는 값 타입은 다루기 쉽습니다. 전달할 때 원래 값이 변경되지 않음을 알고 있기 때문입니다.

inout 매개변수는 원래 값을 변경할 수 있게 하여 값 타입의 장점을 무너뜨립니다.

 

inout 매개변수가 함수 호출이 끝날 때 값을 자동으로 다시 할당한다는 것을 알고 있으므로, 단순히 함수에서 값을 반환하고 직접 할당하여 inout 매개변수 없이도 함수를 작동하게 만들 수 있습니다.

이 방법으로 작성된 함수가 여전히 의미가 있다면, 일반 매개변수를 사용하는 것을 권장합니다. 일반 매개변수를 사용하는 것이 더 직관적이고 이해하기 쉽기 때문입니다.

 

다음은 inout 매개변수를 사용하지 않고 incrementByOne 함수를 작성한 예제입니다.

 

var count = 0
// 1
func incrementByOne(number: Int) -> Int {
    return number + 1
}
// 2
count = incrementByOne(number: count)

print(count)
// print 1