빙수왕의 개발일지

In-Out Parameters, 인아웃 파라미터에 대하여 본문

개발/iOS

In-Out Parameters, 인아웃 파라미터에 대하여

빙수킹 2021. 8. 2. 19:39

Swift Docs를 기반으로 이해한 내용을 작성하였습니다.

In-Out Parameters

함수 파라미터는 default가 상수이다.

상수이므로, 함수 parameter의 값을 함수의 body 안에서 바꾸려고 하면 compile-time 에러가 발생.

parameter의 값을 변경하고 싶다면, 그리고 이 변경이 함수 호출이 끝나고도 지속되길 원한다면, 파라미터를 in-out parameter로 만들어라.

in-out 키워드는 parameter 앞에 배치하여 사용한다.

in-out 매개변수에는 함수에 전달되고, 함수에 의해 수정되고, 원래 값을 대체하기 위해 함수 외부로 다시 전달되는 값이 있다.

상수나 문자(literal value)argument(전달인자)로 사용될 수 없다. (수정할 수 없는 값이기 때문에)

함수를 콜할 때, 변수의 이름 바로 앞에 앰퍼샌드(&)를 배치한다.

 

in-out 파라미터는 default 값을 가질 수 없다.
variadic 파라미터(가변 파라미터)는 inout 으로 표시할 수 없다.

 

예제

아래 예제에서, swapTwoInts(::)를 통해 someInt와 anoterInt의 값이 바뀐걸 알 수 있다.

 

 

in-out 파라미터는 함수에서 값을 return하는것과 다르다.
in-out 파라미터는 함수가 함수의 body 범위 밖에서 영향을 발휘하는 방법 중 하나이다.

 

In-Out Parameters 의 최적화

In-Out parameter들은 다음 과정을 통해 전달된다:

  1. 함수가 호출되면, argument의 값이 복사된다.
  2. 함수의 body 안에서, 아까 복사된 복사본이 변경된다.
  3. 함수가 return할 때, 복사본이 원래 argument에 할당된다(assigned).

이 동작을 copy-in copy-out 또는 call by value result 라고 한다. 예를 들어, 계산 속성(computed property) 또는 observer를 가진 프로퍼티가 in-out 파라미터로 전달되면, 함수가 호출 될 때 getter, 함수가 return될 때 setter가 호출될 것이다.

→ 함수에 들어갈 때 그 값이 copy 되기 때문에 getter가 호출되고, 함수가 리턴될 때 copy되어 변형된 값을 다시 원래 프로퍼티에 넣어주므로 setter가 호출된다는 뜻인듯함.

최적화에서(As an optimization), argument가 메모리의 물리적 주소에 저장된 값일 때, 같은 메모리 위치(location)는 함수 body의 안과 밖 모두에서 사용된다. 이 최적화 동작을 call by reference 라고 한다. call by reference 는 복사의 오버헤드(코스트 정도로 받아들이면 될듯)를 제거하면서 모든 copy-in copy-out 모델의 모든 요구사항을 만족한다. copy-in copy-out에서 제공하는 모델을 사용하여 코드를 짜쇼. call by reference 에 의존하지 마시고요. 그래서 이게 최적화 여부와 관계없이 올바르게 동작할 수 있도록 하십쇼.

→ 이부분 너무 이해가 안돼서 20번도 넘게 읽은거같다.. ㅎ 근데도 아직 잘 모르겠다. 여기서 말하는 최적화라는건, 컴파일 속도에 대한 최적화인 것 같다.(Swift Optimization 구글에 검색했더니 '컴파일러'의 Optimization에 관한 내용, 방법들만 나오니 그런것으로 간주하는중) Project의 Build Setting에 보면 최적화 레벨을 설정할 수 있는데 이에 따라 컴파일 속도가 달라진다.(아래 캡쳐에 있음) 이 최적화 레벨에 따라 call by reference가 될 수도 안될수도 있기 때문에(아마도 최적화 레벨을 올리면 inout키워드로 쓰인 파라미터가 copy-in copy-out이 아닌, call by reference로 진행된다는 것 같음), 이 최적화 레벨과 상관없이 잘 동작하도록 in-out 키워드를 잘 쓰라는 말인 것 같다.

그럼 call by reference는 그럼 무적권 좋은가? 쓸데없이 copy하지 않아 메모리 관점에서는 이득이겠지만, 아마 이 reference로 사용할 때 동일한 주소에 접근하지 않도록 뭔가 .. lock하는 과정이 있을 것 이므로 항상 최고다라고는 할 수 없겠다. 하지만 swift에서 최적화 옵션을 높이면 call by reference를 한다니까 아마 이득이니까 그럴듯?

함수 내에서는, 원래 값이 현재 범위(scope)에서 사용이 가능하더라도 in-out argument로 전달된거에 access 하지 마쇼. 원본에 access하는 것은 동시(simultaneous) 액세스임. 이것은 스위프트의 메모리 독점성 보장(Swift’s memory exclusivity guarantee)을 위반한다. 같은 이유로, 당신은 여러 in-out 파라미터에 같은 값을 전달할 수 없다.

→ 전달된 argument의 original에 한번에 한놈만 접근하자.

→ 맨 마지막 줄은 한 함수에 여러 in-out 파라미터가 있을 때, 여러 파라미터에 같은 argument를 전달할 수 없다는.. 뜻..인듯..

Memory Safety는 멀티쓰레드 환경에서 다양한 쓰레드가 한곳에 접근해도 값이 이상하게 변하지 않음을 보장하는 것이다. 더 자세한 내용은 아래 링크 참조. (다음에 봐야겠다.ㅎ)

https://docs.swift.org/swift-book/LanguageGuide/MemorySafety.html

클로저나 중첩 함수(nested function)에서 사용할 땐 in-out 파라미터가 nonescaping 이여야 합니다.

만약 캡처를 해야한다면, 캡처 리스트를 사용하세요. 캡처 후 값을 변경해야 한다면, 명시적인 local 복사본을 만들어서 함수 return 전에 원래 변수에 넣는 작업을 구현해야 합니다.

→ 위 코드에서는 중첩 함수에서 원래의 a가 변경되지 않도록 캡쳐 리스트를 사용하고 있다.

→ 위 코드는 명시적인 local 복사본을 만들고, defer을 통해서 원래 변수에 넣어주고 있다.

출처

https://docs.swift.org/swift-book/LanguageGuide/Functions.html

https://docs.swift.org/swift-book/ReferenceManual/Declarations.html#ID545