7.2.6 변수의 생존 범위와 생명 주기

변수는 정의된 위치에 따라 사용할 수 있고 생존할 수 있는 일정 영역을 부여받습니다. 이를 변수의 생존 범위 또는 스코프라고 합니다. 영역을 기준으로 보면 전역 변수와 지역 변수로 나눌 수 있을 것입니다.

전역 변수는 글로벌(Global) 변수라고도 하는데, 프로그램의 최상위 레벨에서 작성된 변수를 의미합니다. 일반적으로 프로그램 내 모든 위치에서 참조할 수 있으며, 특별한 경우를 제외하면 프로그램이 종료되기 전까지 삭제되지 않습니다.

반면 로컬(Local) 변수라고도 하는 지역 변수는 특정 범위 내에서만 참조하거나 사용할 수 있는 변수를 의미합니다. 지역 변수는 선언된 블록이 실행되면서 생겨났다가 실행 블록이 끝나면 제거됩니다. 이를 변수의 생명 주기라고 합니다. 다음 예제를 보겠습니다.

do { // 상위 블록
    do { // 하위 블록
        var ccnt = 3
        ccnt += 1
        print(ccnt) // 4
    }
    
    ccnt += 1
    print(ccnt) // 오류 - "Use of unresolved identifier 'ccnt'" 
}

do 블록은 일반적으로 do~catch 구문으로 사용되지만, 단독으로 사용되면 단지 실행 블록을 구분하는 역할을 합니다. do 블록은 중첩해서 사용할 수 있는데, 이때 내부에 중첩된 do블록을 기준으로 실행 블록은 단계화됩니다. 내부에 많이 중첩되어 있을수록 하위 블록입니다.

위 코드의 아래 부분에서 왜 오류가 났을까요? ccnt라는 변수는 하위 블록에서 선언되었고 해당 코드가 종료된 이후 메모리에서 해제되고 존재하지 않는 변수가 되기 때문입니다. 만약 해당 위치에서 ccnt를 출력하기 위해서는 수정이 필요합니다. 아래 코드를 보시겠습니다.

do { // 상위 블록
    var ccnt = 0 // <- 옮긴 위치
    do { // 하위 블록
        ccnt = 3
        print(ccnt) // 3
    }
    
    ccnt += 1
    print(ccnt) // 4 
}

ccnt는 더 넓은 범위의 블록에서 정의되었으며, 하위 블록에서는 상위 블록의 변수를 얼마든지 가져다 사용할 수 있기 때문에 오류가 발생하지 않습니다.

이때 주목할 점은 0으로 초기화를 했다는 점입니다. 변수가 생성된 블록이 아닌 다른 블록에서 사용하려면 반드시 초기화되어 있어야 합니다. 하나의 블록에서 다른 블록으로 참조에 의한 전달 과정이 일어날 때 변수의 주소값이 필요합니다. 만약 초기화되지 않았다면 메모리를 할당받지 못한 상태이므로 주소값도 존재하지 않습니다. 따라서 오류가 발생합니다.

전역 변수를 알아보겠습니다. 전역 변수는 최상위 블록에서 선언된 변수이므로 그보다 하위 블록인 함수 내부에서도 얼마든지 접근할 수 있습니다. 예제를 보겠습니다

var count = 30

func foo() -> Int {
    count += 1
    return count
}

foo() // 31

이 함수는 내부에서 count 변수를 1만큼 증가하는 처리를 합니다. count는 상위 블록인 전역 범위에서 선언된 변수입니다. 함수 내에서는 전역 변수의 값에 접근할 수도 있고, 수정할 수도 있습니다.

이번에는 전역 변수와 지역 변수가 겹칠 때는 어떤 현상이 발생하는지 알아봅시다.

var count = 30

func foo(count: Int) -> Int {
    var count = count
    count += 1
    return count
}

print(foo(count: count)) // 함수 내부의 count 변수값 : 31
print(count) // 외부에서 정의된 count 변수값 : 30

함수 외부에서 count 변수는 전역 범위로 선언되어 있고 함수 내부에서도 count가 매개변수로, 그리고 지역 변수로 선언되어 있습니다. 왜 두 값을 print 했을때 차이가 있을까요?

스위프트에서 함수의 외부와 내부에 각각 같은 이름의 변수가 존재하면 내부에서 선언된 변수는 외부와 상관없이 새롭게 생성된다는 것을 알 수 있습니다. 그렇지 않다면 외부 변수의 값도 함께 변경되었을 것입니다.

위의 예에서 내부 영역에 정의된 count 변수는 지역 변수(Local Variable)로서, 외부에서 정의된 count 변수와는 엄연히 다른 객체입니다. 또한, 외부와 내부에서 같은 이름의 변수가 선언되면 변수 사용의 우선순위에 따라 외부 변수가 아닌 내부 변수를 사용하게 됩니다.

함수처럼 블록 내부에서 변수나 상수가 사용될 경우 컴파일러는 이 변수가 정의된 위치를 다음의 순서에 따라 검색합니다.

  1. 함수 내부에서 정의된 변수를 찾음

  2. 함수 외부에서 정의된 변수를 찾음

  3. 글로벌 범위에서 정의된 변수를 찾음

  4. import된 라이브러리 범위

Last updated