Xcode

[WWDC24] Breakpoint 새롭게 안 사실들 정리

Zedd0202 2024. 6. 30. 22:37
반응형

 

오.... Run, Break, Inspect: Explore effective debugging in LLDB 을 보면서 처음알았던 사실이 있어서 메모해둔다. 

다 breakpoint관한 내용임 

 

# Breakpoint

대충 이런코드가 있다고 생각해보자. 

Button의 생성자 쪽에 breakpoint를 걸어놨으므로 Button이 생성될 때 프로그램이 멈추면서 걸릴것이다.

이렇게!

위 코드는 Hello, world!라는 Button을 누르면 someMethod가 실행되는데, 버튼을 한번 눌러보겠다!!!

그럼 이렇게 breakpoint가 걸린다.

여기서 궁금증이 들 수 있는데, 

20번째 라인에 breakpoint를 걸어놨는데 왜 21번째 라인에서도 걸린것인가? 이다.

 Run, Break, Inspect: Explore effective debugging in LLDB 에서 요 이유를 명쾌하게 설명해줘서 알게되었다!! 

 

breakpoint를 걸면 breakpoint navigator에  

이렇게 2개의 breakpoint가 있는 것을 볼 수 있다.

여기서 나오는 것들은 서로 다른 코드 경로를 통해 이 breakpoint에 멈출 수 있음을 나타낸다고 한다. 

이 경우 Xcode는

1. Button이 생성될 때 (20번째 라인)

2. Button 생성자 자체에 의해 호출된 trailing closure가 실행될 때 

를 잡은것이라고 볼 수 있다.

그리고 breakpoint마다 ID가 생성되는데

이 경우에는 1로 할당이 된거고, 각 breakpoint는 1.1, 1.2이런식으로 할당이 된다. 

그래서 breakpoint 1.1, breakpoint 1.2 이런식으로 나온것을 볼 수 있다!!

 

원래 navigator에서도 아래와같이 ID를 바로 볼 수 있는 것 같은데

이게 언제 나오고 언제 안나오는지 모르겠다.. 길어서 그런가... 

1.n은 ID가 안나옴 ㄱ- 

 

action이 있는 Button을 만들어보자

@State private var isAddedToWatchLater = false

Button(action: { self.isAddedToWatchLater.toggle()}) {
    Text(isAddedToWatchLater ? "Added to Watch Later" : "Add to Watch Later")
        ...생략
}

이때 Button에 breakpoint를 걸게되면 

Xcode는 3개의 breakpoint를 생성한다. 

 

1. Button의 생성자 (1.1)

2. Button의 action closure (1.2) 

3. Button의 trailing closure (1.3) 

(불리는 순서는 1 -> 3 -> 2임)

 

# LLDB에서 breakpoint list확인하기

lldb에서도 확인이 가능하다. (navigator에서 나오는것보다 더 자세한 정보를 알 수 있음) 

breakpoint list를 입력하면 

가독성은 좀 글킨한데;; 아무튼 이렇게 볼 수도 있음 ㅎ

 

# Disable하기

물론 disable도 가능하다!

navigator에서 저 빨간 박스 부분을 클릭하면 탁한 파란색이 되면서 disable처리가 된다.

(우클릭 눌러서 Disable Breakpoint Location눌러도됨) 

 

그동안 딱 내가 지정한 라인 말고 다른 라인이 걸릴때 무지성으로 continue버튼 누르고 그랬는데 이런 이유가~~~!!!

 

# Swift Error breakpoint

예를 들어서 이런 Json String이 있고 

let jsonData = self.jsonString.data(using: .utf8)!

do {
    let videos = try decoder.decode([Video].self, from: jsonData)

    for video in videos {
        print(video.title, video.description)
    }
} catch {
    // do nothing
}

해당 Json을 파싱하는 코드가 있다. 

문제인 상황을 만들어보자. 

id 10번의 imageName Key에 오타를 냈다. image -> imag

 

자.. 그럼 여기에 오타가 있는지 몰랐다고 가정하고 원인을 파악한다고 해보자. 

1. 프로그램을 실행했더니 

2. 파싱에 실패했고

3. 공교롭게도 error를 찍고 있지 않아서 원인을 찾다가 

4. decode로직에 breakpoint를 찍었다

그럼 Json의 객체 개수만큼 breakpoint가 걸릴텐데....다들 이런 경험 한번씩 해봤을 것 같은데 (머쓱)

이럴 때 Swift Error Breakpoint를 사용하면 유용하다!!!

Swift Error Breakpoint는 Swift Error가 발생하면 프로그램을 중단하도록 lldb에 지시한다고 한다. 

 

1. 기존에 추가했던 breakpoint를 지우고

2. 왼쪽에서 breakpoint navigator로 이동해서

3. 왼쪽 하단의 +버튼 > Swift Error Breakpoint 를 추가한다. 

4. 실행!

그럼 이렇게 문제가 되는 (Swift Error가 처음 발생한) 라인에서 멈추게 된다. 

lldb를 사용해서 이때의 id를 알아내고 

아 그럼 id 10번에 무슨 문제가 있나보다~ 

하고 때에 맞는 적절한 수정을 해주면 된다.

 

이 경우에는 바로 Json String의 id 10번을 찾아서 

imageName쪽을 살펴봐주고 -> 오타가 있었군? -> 수정 

해주면 끝나는 것이다.

 

나도 이렇게 decode쪽 로직에 breakpoint를 걸었던 적이 있었고.. 당연하게도 매번 breakpoint가 실행되니 그냥 print로 원인을 찾았던 적이 있는데 이럴 때 Swift Error Breakpoint가 유용했을 것 같다.

 

물론 이건 Error가 나야하는 거라서!!! 상황에 따라 적절하게 사용하면 될 것 같다. 

 

# High-firing Breakpoint

High-firing Breakpoint는 자주 발생하는 breakpoint라는 뜻이다. 

for-loop에 걸어놓은 breakpoint가 대표적이다.

우리가 방금 본 decode로직쪽에 걸어둔 breakpoint도 High-firing Breakpoint라고 볼 수 있다.

 

for-loop안에 breakpoint를 걸면 모든 반복에서 멈추는 것 보다.. 특정한 이벤트가 발생했을때만 중단하면 좋은 경우가 대부분이다.

High-firing Breakpoint를 처리하는 3가지 기술을 WWDC에서 소개했는데,

condition같은건 대부분 알 것 같긴하지만 처음 들어보는 것도 있어서 간단히 정리해본다.

 

1. Breakpoint conditions

이렇게 for-loop안에 breakpoint를 걸어두었는데 

특정 조건을 만족하는 경우에만 중단하고 싶다면 

condition을 원하는대로 추가해주면된다.

나는 description의 count가 30이 넘는 경우만 멈추고싶다! 로 한것이다.

 

2. Breakpoint action

for video in videos {
    if video.hasRemoteMedia {
        video.loadRemoteMedia()
    }
    processSomething()
}

예를 들어 이런 코드가 있다고 생각해보자. 

processSomething()은 매 반복마다 호출될텐데, loadRemoteMedia가 실행된 경우에만 중단되길 원할수도 있다.

 

1번에서 본 것 처럼 condition을 사용해도 되지만 Breakpoint action을 사용해볼 수도 있다.

이런경우 tbreak(temparary(임시) breakpoint.) 를 만들수 있음. 

그럼 loadRemoteMedia가 실행된 경우에만 processSomething()에 중단하고 싶은거니까 

loadRemoteMedia쪽에 breakpoint를 걸고 ➡️ Edit breakpoint  ➡️ Add action  ➡️ Debugger Command 

tbreak {filename:linenumber} 또는 tbreak {functioname} 를 적어주면 된다. 

tbreak는 해당 위치에서 한번만 프로그램을 중단시킨다고 한다. 

(우리는 for-loop의 loadRemoteMedia의 action에 걸어놨으므로 loadRemoteMedia가 불릴 때 마다 임시 breakpoint가 생성되어 processSomething에서도 한번 중단하게 된다.) 

 

실행해보면 

이렇게 one-shot breakpoint라고 나옴.

 

tbreak는 일회성 breakpoint라는 것이 핵심인데, 예를 들어 for-loop가 아니라 

viewDidLoad에서 processSomething에 tbreak를 걸었다고 하자. 

(tbreak {filename:linenumber}도 가능한데 이번에는 tbreak {functioname}로 해줘봤음)

processSomething()이 아니라 processSomething으로 적어줘야함

1. processSomething에는 임시 breakpoint가 걸리고

2. 최초로 processSomething이 호출되면

3. 중단

4. 그 이후에는 breakpoint가 삭제되어서 중단 X 

인 것~~!!

 

사진처럼 Edit breakpoint에 들어가서 하는 방법도 편하긴한데.. 나는 그냥 lldb에서 하는게 더 편했다.

중단됐을 때 그냥 lldb에서 tbreak생성 해버리기 

 

3. Breakpoint ignore count

고정된 횟수동안 breakpoint를 무시하고 이후에 발생하는 것만 중단시키는 방법이다.

무시하고 싶은 고정된 횟수의 count를 ignore부분에 넣어주면 된다!

 

-------

 

tbreak는 처음봤는데, 진짜 일회성으로 뭔가 확인하고 싶을 때 유용하게 쓸 수 있을 것 같다 ㅎㅎ

끝! 

 

참고 : Run, Break, Inspect: Explore effective debugging in LLDB

반응형