[SWIFT] defer 후처리

2021. 2. 16. 01:34SWIFT

반응형

오늘은 SWIFT 문법 중 defer 구문에 대해 알아보겠습니다.

 

저도 한 번도 사용해 볼 생각이 없었던 구문인데요.

사용하는 프로젝트를 보게되어서 한 번 공부해 보았습니다.

 

defer란

defer구문으로 작성하게 되면 코드 블럭을 나가기 전에 꼭 실행되게 됩니다. 즉, 어떤 식으로 코드 블럭을 빠져나가게 되도 꼭 실행됩니다. 예로 오류가 발생해도 빠져나가기 전에 실행되고 정상적으로 코드가 작동해도 빠져나가기 전에 실행됩니다.

func example() {
    defer { print("World!!") } 
    print("Hello")
}

/*
Print:

Hello
World!!
*/

다음과 같이 World가 먼저 출력될 것이라고 예상했으나 defer 구문으로 작성하면 생각과는 다르게 실행됩니다.

우선 Hello가 출력되고 코드 블럭이 끝나기 전 defer 구문인 World가 출력됩니다.

 

이제는 defer가 어떤 때에 호출되고 실행되는지 쉽게 알 수 있었죠?

 

그렇다면 defer가 여러개가 한 코드블럭에서 사용되면 어떻게 될까요...?

예제로 알아볼게요.

func example() {
    defer { print("1") }
    defer { print("2") }
    defer { print("3") }
    defer { print("4") }
    defer { print("5") }
    defer { print("6") } 
}

/*
Print:

6
5
4
3
2
1
*/

어때요...? 예상과 똑같나요? 뭔가 이상하죠 반대로 출력된 느낌이죠...

원래대로라면 먼저 들어간 순서대로 처리가 되야할건데 왜 그럴까요...?

 

바로 Stack으로 동작하기 때문이에요. 

즉, 가장 먼저 들어간 구문이 가장 Stack의 밑에 위치하기 때문에 가장 늦게 출력됩니다.

 

이제 동작원리까지는 아시겠죠.

다음으로는 조금 더 복잡한 구문의 실행이 어떻게 될지 알아볼게요.

func example() {
    defer { print("1") }
    
    do {
        print("2")
        defer { print("3") } 
    }
    
    defer { print("4") }
    
    for num in 0...2 {
        defer { print("5") }
        
        if num % 2 == 0 {
            defer { print("6") }
            print("7") 
        }
    }
    
    defer { print("8") }
}

/*
Print:

2
3
7
6
5
5
7
6
5
8
4
1
*/

 

아까보다는 조금 복잡합니다.

주의해야할 점은 defer는 하나의 코드 블럭이 끝나기 전에 실행된다는 점입니다. 그렇다면 코드블럭은 어디어디에 해당할까요?

 

우선, do 구문은 하나의 코드 블럭입니다. 그렇기 때문에 먼저 "2"가 출력되고 빠져나오기 전에 "3"이 출력됩니다.

다음으로 for문의 한 번의 실행도 하나의 구문입니다. 그렇디 때문에 num == 0일 때, "7"이 출력되고 if 문이 끝나기 전 "6"이 출력되고 for문의 한 번이 끝나기전 "5"가 출력됩니다.

 

그렇다면 지금 순서는 2, 3, 7, 6, 5가 되죠?

 

이제 for문이 한번 더 돌 때, num == 1이죠. "5"만이 출력되고, 다음으로 num == 2일 때는 "7", "6", "5"가 출력됩니다. 이제는 for문이 종료되고 마지막으로 가장 바깥의 defer 구문들이 실행될 차례입니다. Stack처럼 쌓여서 실행된다고 했죠?

 

즉, 가장 마지막에 들어온 "8"이 먼저 출력되고 순서대로 "4", "1"이 출력됩니다.

 

지금까지의 순서는 5, 7, 6, 5, 8, 4, 1이죠?

 

다 합치면 2, 3 ,7, 6, 5, 5, 7, 6, 5, 8, 4, 1이 되게 됩니다.

 

 

defer가 호출되지 않는 경우

어떤 구문이 끝나기 전에는 꼭 defer 구문이 불려서 실행된다고 했지만 실행되지 않는 경우도 있습니다. 엄청나게 특별한 경우는 아니고 그냥 보면 당연히 생각할 수 있는 부분입니다.

 

예시로 바로 알아볼게요.

func example() throws -> Int {
    defer { print("1") }
    
    enum ExampleError {
        case error
    }
    
    throw ExampleError.error
    defer { print("2") }
    
    print("3")
}

example()

/*
Print

1
*/

 

보면 Error를 던지는 뒤에 구문은 실행되지 않았습니다. 원래라면 함수 구문이 끝나기 전에 꼭 실행되어야 하는게 defer이지만 다르죠..?

 

혼란스러울 필요없습니다! 당연히 구문이 끝나기 전에 호출이 되었다면 Stack에 할당되어 구문이 끝나기 전에 호출되겠지만 그 전에 이미 끝나버려서 할당되지 못했기 때문에, 들어간 "1"만이 호출되고 종료되는 것입니다.

 

어떤 defer 구문이던 호출되기 전에 구문이 죽어버리면 Stack에 할당되지 못하기 때문에 못불리는 것입니다.

 

다른 예시도 하나 보겠습니다.

func example() {
    defer { print("1") } 
    guard false else { return }
    
    defer { print("2") }
}

example()

/*
Print

1
*/

역시 여기 구문에서도 guard 구문으로 인해 defer가 들어가기 전에 함수가 종료되어서 "1"만 출력되는 것을 알 수 있죠..?

 

중요한 것은 Stack에 할당되기 전에 구문이 종료되어 버리면 defer 구문은 아무 효력이 없다는 것을 기억하면 될 것 같습니다.

 

 

잘 사용하지 않는 구문이라 전혀 사용할 생각을 안하고 있었는데, 언젠가 필요한 순간이 오면 꼭 사용해봐야겠습니다..‼️

SWIFT에서 모르는 구문이 없을 때까지 열심히 해야겠다는 생각이 드네요.

오늘은 defer 구문에 대해 작성해봤는데, 많은 도움이 됐으면 좋겠네요 :)

 

잘못된 점이나 더 알고싶은 것이 있으면 댓글 달아주세요.

감사합니다 ( _ _ )

 

반응형

'SWIFT' 카테고리의 다른 글

[SWIFT] OptionSet  (0) 2021.03.07
[SWIFT] Swift SOLID 원칙  (0) 2021.02.27
[SWIFT] Set  (0) 2021.01.28
[SWIFT] where 절  (0) 2021.01.19
[SWIFT] Patterns, Pattern Matching - 패턴과 패턴매칭 (2)  (0) 2021.01.16