설명이 부족한 공식 문서 때문에 실무 개발자가 궁금해할. 내가 궁금하니 

내용 중 하나.

https://docs.swift.org/swift-book/ReferenceManual/Patterns.html#grammar_value-binding-pattern

공식문서의 내용에서 쓰는 let point는 튜블 패턴이다.


그런데 튜플은 튜플대로 사용하면 되는데 굳이 switch 문에 다시 바인딩을 시키는 것이 의아하다. 왜냐면 switch 문은 다양한 case에 따라 if 문의 개수를 줄이는데 유용하고 C언어에서 어셈블리로 변화했을 때 switch case문이 다중 if문에 비해 속도가 빠르듯이 더 나은 성능을 위해 switch를 쓰는데 하나의 case문이라면 의미가 없기 때문이다.


인자 개수에 따라 처리가 가능하다면 유용할 텐데 그러지 않다. switch 문의 인자 개수는 동일해야 한다. 만약,


let point = (3, 2, 4)


switch point {

// Bind x and y to the elements of point.

case let (x, y):

    print("The point is at (\(x), \(y)).")

case let (x, y, z):

    print("The point is at (\(x), \(y), \(z)).")

}


이런 예제라면 에러 난다. 그러면 상수값과 섞어 쓸 때 의미가 있게 된다.


        let point = (3, 2)


        switch point {

        // Bind x and y to the elements of point.

        case (3, let y):

            print("The point is at  \(y).")

        case (let x, 2):

            print("The point is at \(x).")

        default:

            print("default")

        }


혹은, 

        let point = (3, 2)

        let point 2 = (3, 8)

        let points = (point, point 2)


        switch points {

        // Bind x and y to the elements of point.

        case ((3, 2), let y):

            print("The point is at  \(y).")

        case (let x, (0, 0)):

            print("The point is at \(x).")

        default:

            print("default")

        }

이런 식이다.  swift는 break문이 필요하지 않아 상위 case에서 참이 되어 버리면 하위 case는 쓸모가 없게 되긴 하지만 튜플 패턴에서 일정 값만 거르고 해당 값을 binding 해서 쓸 때는(굳이 binding 안 하고 써도 되겠지만) 써도 된다는 뜻.


여기서 let을 var로 바꾸었을 때 바딩 된 값이 ref 값인지 copied 값인지 궁금해진다. 


        let point = (3, 2)

        var point 2 = (3, 8)

        let points = (point, point 2)


        switch points {

        // Bind x and y to the elements of point.

        case ((3, 2), var y):

            print("The point is at  \(y).")

            y = (4, 4)

            print("The point is at  \(y).")

        case (let x, (0, 0)):

            print("The point is at \(x).")

        default:

            print("default")

        }


        print("point 2 = ", point 2)


The point is at  (3, 8).

The point is at  (4, 4).

point 2 =  (3, 8)


복사본이 전달된다. call by reference로 볼 수 있는데 call by assignment 방식 때문에 객체의 경우도 따로 조사를 해봐야 한다. swift에서 class init 후 전달되는 모든 값은 참조형으로 작동하기 때문에라도 한번 더 봐야 한다. 이 말은 3년간 Objective-C 할 때는 함수 포인터보다는 NSNotificationCenter를 통하여 각기 다른 객체(여기서는 파일이라고 생각하면 되겠다)에 메시지를 전달하여 해당 함수를 동작시켰다. 파라미터 전달 방식은 프로그램이 커지고 빌더 패턴이 하나라도 끼인 상태라면 전달이 상당히 복잡해져서 요구사항이 바뀐 경우 그냥 노티피케이션센터에 메시지 하나 더 정의해서 전달했다는 뜻이다. 물론, 안드로이드로 치면 해당 액티비티가 가진 멤버 자료들이 resume 되는 시점에 제대로 복구되는지 상관 않고 기능 구현만 했다는 뜻이다. 대부분 블랙박스 검증이니까 얼추 잘되면 그냥 오케이다. 그러나 의료 앱을 만드는 지금은 웬만한 자료는 가지고 있지 않는다. 함수형 프로그램의 철학대로 데이터의 흐름에만 집중을 한다. 가끔 저렇게 요구사항은 바뀌고 급하게 검증을 돌려야 하는 상황이면, Call by Reference를 십 분 활용하여


var pDashboard : DashboardScrollView? = nil


이렇게 꼭 필요한 자료가 있는 싱글톤에 정의하고, 


GS.s.pDashboard = self


만든 객체를 넣고


GS.s.pDashboard?. topScroll()


어디서든 호출할 수 있게 한다. 참고로  swift에서 싱글톤은

private init()

static let s

으로 만든다. 사실, 나의 실무 프로그래밍 책에 쓴 것럼 동시성을 체크해야 하지만 네트워크 상황에서도 DispatchQueue 를 전혀 쓰지 않고 있기 때문에 문제는 없어 보인다. closure를 이용하면 된다. (더블클로저를 이용한 프로그래스바 참조) 물론, 운영체제 혹은 프로세서의 멀티 프로세싱에서 앱이 따로 thread를 만들지 않으면 앱 실행 순서는 리니어 하다는 것을 가정해야 하는 위험이 있긴 하지만 다른 안정 장치로 의료용 앱을 보호해야 한다.


자 다시, call by assignment 로 돌아가자. 튜플은 값을 주고, 튜플도 객체니 그냥 call by reference로 믿어도 되겠지만 결국 책임은 실무 프로그래머에게 있으니 의심해 볼 수 밖에 없다.


class dummy {

    var A : Int = 3

}


더미 class를 만들자.


        var a = dummy()


        switch a {

        case var b :

            print("a.A = \(a.A)")

            print("b.A = \(b.A)")

            b.A = 2

            print("a.A = \(a.A)")

            print("b.A = \(b.A)")

        default:

            print("defult")

        }


        print("a.A = \(a.A)")


a.A = 3

b.A = 3

a.A = 2

b.A = 2

a.A = 2


바뀐다. 그럼 이 경우는?


        var a = dummy()


        switch a.A {

        case var b :

            print("a.A = \(a.A)")

            print("b = \(b)")

            b = 2

            print("a.A = \(a.A)")

            print("b = \(b)")

        default:

            print("defult")

        }


        print("a.A = \(a.A)")


a.A = 3

b = 3

a.A = 3

b = 2

a.A = 3


안바뀐다.


결국, 파이썬처럼 call-by-assignment 로 동작하는 것을 볼 수 있다.


이제 switch 바인딩의 의미가 값 결정에 있지 않고 단순 커넥션이며, 객체에 따라 다르게 assignment로 동작한다는 것을 알 수 있다. swift는 직관적 리딩을 위해 파라미터 까지도 생략하려면 _(under score)를 써야 하게 해놨는데 복잡하게 프로그래밍 하는 것보다는 직관적으로 알 수 있게 하는게 나을 것이다.


그러나, 난 어렵게 프로그래밍 하는 것을 추천한다. 거대한 switch 문을 만들길 바란다. 대한민국은 프로그래머보다 경영자가 우대받는 사회니까. 우리팀이 이기길 바라는 것은 팀원으로 당연한거니까.


띄워쓰기가 맘에 안들수도 있겠다. 귀차니즘에 의해 Xcode의 ^I 정렬 방식을 따르고 브런치의 맞춤법 검사를 필터링 없이 돌린다.(소스에도 걸리는 것을 보니 ...)


혹 구독자를 위해 요약하면,


Swift의 value-binding pattern은 call by assignment 로 동작한다는 것. 물론, siwft 4.2 기준이며 5가 되었을 때도 공식문서에 별다른 말이 없으면 다시 확인해야 한다. product랑 가까운 개발자 일수록 이런 말 못할 고민은 늘어간다. 비트 하나 차이로 사람이 죽을수도 있으니... 늘 낮은 자세로 탐구하고 고민하고 책임져야 한다. ㅠㅠ

+ Recent posts