달력

11

« 2018/11 »

  • 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

 함수내에서, 값(Value)으로 전달받은 인자(Argument)는 참조(Reference)로 전달받는 인자와 차이가 있다. 참조로 전달받은 인자는 변경이 일어났을 때 본래의 객체도 같이 변경되지만 값으로 전달받은 인자는 그렇지 않다.

 

 

1
2
3
4
5
var integer = 5
func multiply(integer: Int, multiplier: Int) {
    //error, integer is defined as a let.
    integer = integer * multiplier
}
cs

 
 애초에 값으로 전달받은 인자는 상수라서 변경조차 불가능하다. 값으로 전달받은 인자를 함수 내에서 수정하려면, 함수의 매개변수(Parameter)에 'inout'이라는 키워드를 붙이면 가능하다.

 

 

1
2
3
4
5
6
7
var integer = 5
func multiply(integer: inout Int, multiplier: Int) {
    integer = integer * multiplier
}
multiply(integer: &integer, multiplier: 2)
print(integer) //10
 
cs



 매개변수에 inout 키워드가 붙었으니, multiply에는 integer의 참조가 전달된 것일까? 

inout Parameter에 대한 Apple의 설명을 보면 이야기가 좀 다르다.



In-Out Parameters: In-out parameters는 다음과 같이 전달됩니다.
1) 함수가 호출됬을 때, 값으로 전달받은 인자는 복사(Copy)됩니다.

2) 함수 내에서는 복사본이 변경됩니다.

3) 함수가 반환(Return)될 때, 복사본의 값이 본래의 값에 할당(Assign)됩니다.

 

 

 Apple의 설명을 확인하기 위해, 간단한 코드를 작성하면...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var number: Int = 2 {
    didSet {
        print("number was assigned.")
    }
}
 
func function(arg: inout Int) {
    print(number) //2
    arg = 5
    print(number) //2
    arg = 10
}
 
function(arg: &number) //"number was assigned."
 
cs

  

 function 내에서 전달받은 인자를 5로 바꾸어도, number는 변하지 않는다. function이 호출된 다음 number의 didSet이 호출되는 것을 볼 수 있는데, 이것을 통해 function 반환 후 본래의 값에 새 값이 할당된다는 것을 확인할 수 있다.

 

 이것은 참조가 전달된 Call-By-Reference와 다르며, Copy-In Copy-Out 또는 Call-By-Value Result라고 불려진다.

 

 

※ 2023.01.24에 추가된 내용

 Swift Docs에 적혀있는, In-Out Parameters의 Optimization에 따르면 Physical Address에 위치한 변수를 In-Out Parameter의 arg로 전달하면 Call-By-Reference로 동작한다고 적혀있다(Call-By-Reference는 Copy로 인한 Overhead는 없지만 Copy-In Copy-Out의 결과와 동일하다).

 

 Compilier의 Optimization Level도 바꾸어 보고, 코드도 이리저리 수정해가며 테스트를 해보았지만... In-Out Parameter를 정의한 함수에서 Call-By-Reference로 동작하는 것을 확인하는 데에 실패했다. Compiler Optimization 결과에 따라 In-Out Parameter의 동작이 달라질 수 있는 것 같으니, 인위적으로 상황을 재현하는 것은 어려울 것 같다.

 

 Swift Docs에 적힌대로, In-Out Parameter를 사용한 함수를 작성할 때, Call-By-Reference를 고려하지 말고 Copy-In Copy-Out로 동작한다고 생각하고 작성하자.

:
Posted by syjdev

 몇년전 코드 리뷰 날, 선배 개발자 한분이 새로운 것을 보여주셨었습니다.

Objective-C의 코드를 두 개 이상의 파일로 나누는 것이었는데요. 

지금은 Swift로만 개발해서 활용하고 있지 않지만, 기록 차원에서 글로 작성해보았습니다.



 샘플로 만든 프로젝트의 'Project Navigator'는 아래와 같습니다.



 특이하게도 ViewController+DiviedFile.m이라는 파일이 있는데요, ViewController.m에 

넣으려던 코드를 별도의 파일로 분리한 것입니다. 



어떤식으로 분리한 것인지 보기 위해 ViewController.m의 코드를 살펴보면 아래와 같습니다.



 FILE_DIVIDE_FLAG라는 상수가 선언되어 있고, 

마치 헤더 파일을 추가한 것처럼 ViewController+DiviedFile.m을 추가했습니다.



 ViewController+DiviedFile.m의 코드도 볼께요.



 매크로를 사용해서, 상수 존재 여부에 따라 @implementation과 @end를 해당위치에 삽입하는 코드가 있습니다. @implementation과 @end가 있어야 ViewController+DiviedFile.m을 정상적인 형태를 갖춘 코드로 인식하고, 컴파일이 될때는 @implementation과 @end가 없어야해서 지시자(Directive)를 저런 형태로 작성한 것입니다.


 여기까지 작업하고 빌드를 하면, 심볼이 중복된다는 에러를 볼 수 있습니다. 해결하려면 ViewController+DiviedFile.m를 컴파일 대상이 되지 않게 하면 됩니다(전처리 단계에서 ViewController.m에서 ViewController+DiviedFile.m의 내용을 해당 위치에 추가하기 

때문에 괜찮습니다.).



 Xcode에서는 Target -> Build Phases -> Compile Sources에서

ViewController+DiviedFile.m를 지워주면 됩니다.



:
Posted by syjdev