스파르타 코딩 클럽 - iOS 스타터 6기/본 캠프

44. 스파르타 코딩 클럽 - Xcode Instruments, 동기 / 비동기

seongpil Heo 2025. 5. 8. 17:04

  💻  Xcode Instruments, 프로파일링

▪️ Xcode Instruments 란

  • Xcode 에서 제공하는 앱의 성능 분석 및 디버깅 도구
  • 앱 개발자라면 메모리 릭이 나지 않는 앱을 개발해야 하므로, 메모리 릭에 대한 분석을 할 때 Xcode Instruments를 활용하면 좋음
  • Xcode Instruments 를 통해서 성능 분석, 메모리 릭 분석을 할 수 있음
  • 이번 강의에서는 Xcode Instruments 를 활용해서 메모리 릭을 잡아내는 공부를 해봅니다.
  • 메모리 릭 분석을 하면서 스택 트레이스(Stack Trace) 도 함께 확인 가능 

 

Stack Trace

스택(Stack)을 추적(Trace)한다.

코드를 실행하면 스택에 쌓이면서 코드를 수행하게 된다. 이를 콜 스택(Call Stack)이라 함.

코드를 콜 한 순서를 알 수 있는 스택.

이 스택을 뒤돌아보며 코드가 수행된 기록을 훑어보는 것을 스택 트레이스라고 한다.

 

프로파일링

앱의 성능을 분석하고 최적화하는 과정을 프로파일링(Profile) 이라고 한다.


  🧑‍💻  Xcode Instruments 로 메모리 누수 잡기

▪️ Xcode Instruments 사용 실습

앱의 사이즈가 커지고, 프로젝트에 작성한 코드량이 많아질수록, 메모리 릭이 발생하는 곳을 파악하기 힘들 수 있습니다. 예를 들어, 규모가 있는 회사에서 여러 명이 하나의 앱을 함께 개발한다고 했을 때, 내가 작성한 코드 부분이 아닌 다른 사람이 작성한 코드 부분에서 메모리 릭이 발생할 수도 있습니다. 직접 코드를 살펴보며 메모리 릭이 나는 곳을 찾는 것도 좋지만, Xcode Instruments를 활용해서 찾아낼 수도 있습니다.

 

1. 메모리 누수가 나는 상황 연출

  • 간단하게 버튼 하나를 만들고 버튼을 클릭했을 때 메모리 누수가 나도록 코드 작성
import UIKit

class ClassA {
    var x: ClassB?
}

class ClassB {
    var x: ClassA?
}

class ViewController: UIViewController {
    
    @IBOutlet weak var button: UIButton!
    
    @IBAction func buttonTapped(_ sender: Any) {
        let classA = ClassA()
        let classB = ClassB()
        
        classA.x = classB
        classB.x = classA
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}
  • 메모리 누수가 나는 상황은 순환 참조가 나는 상황이라고 배웠으므로

       ClassA ↔ ClassB 가 서로를 순환 참조하도록 설정

 

2. Xcode Instruments 실행

  • 코드 작성 후 Run
  • Product → Profile 클릭하면 Xcode Instruments 열림
  • 초록색 체크 표시: 메모리 누수가 발생하지 않음
  • 빨간색 X 표시: 메모리 누수가 발생함

  • Leaks → CallTree 선택 하면 콜 스택 확인 가능.
  • 가끔 Xcode Instruments 가 콜 스택을 불러오는데 시간이 걸릴 때가 있으니 주의.
  • 이럴 때는 Xcode Instruments 정지 후 Leaks와 Call Tree 탭을 왔다 갔다 하면 로드가 됨. (버그)

 

  • 아래 Call Tree 에서 Invert Call Tree , Hide System Libraries 클릭하면 콜 스택 쉽게 확인 가능
  • 콜스택 아래서부터 순서대로 읽어보면, AppDelegate에서 앱 시작 → ViewController.buttonTapped(_:) 실행 → ClassA에서 메모리 릭 발생

  • 메모리 릭 발생한 콜스택에서 우클릭 → Reveal in Xcode 클릭 시 Xcode 상에서 메모리 릭이 발생한 부분 확인 가능

  • Reveal in Xcode 를 클릭했더니 메모리 누수가 발생하는 원인인 ViewController.swift 파일로 이동하게 됨.

  🔃  프로세스와 스레드

▪️ 프로세스와 스레드 이해

프로세스와 스레드는 모두 컴퓨터에서 작업을 수행하는 흐름, 단위입니다. 실행중인 iOS 앱을 프로세스라고 부를 수 있으며, 프로세스 내부에는 여러 개의 스레드가 존재합니다. iOS 개발뿐 아니라, 일반적인 개발자라면 반드시 알아야 하는 프로세스와 스레드에 대해 공부합니다.

  • 프로세스: 실행중인 프로그램을 프로세스라고 한다.
    • 즉, 실행하기 위해 메모리 위에 올라온 프로그램을 프로세스라고 한다.
    • iOS 앱 개발을 했을 때의 결과물인 앱 도 iOS 운영체제에 속한 프로그램이다.
    • iOS 앱을 실행하면 프로세스라고 부를 수 있다.
  • 스레드: 프로세스 내에서 작업을 수행하는 단위.
    • 한 개의 프로세스 내에서 여러 개의 스레드가 동시에 작업을 수행할 수 있다.
    • 즉, 실행 중인 iOS 앱을 프로세스라고 표현하므로 iOS 앱이 실행중일 때 여러 가지 스레드가 동시에 일을 한다.
    • 크게 메인 스레드와 백그라운드 스레드로 구분할 수 있다.
    • 여러 개의 스레드를 가지고 동시에 작업을 하는 것을 멀티 스레딩이라고 한다.
    • Thread = 스레드라고도 함.

  🔂  동기, 비동기

▪️ 동기와 비동기 이해

  • 동기 = synchronous = sync→ 직렬적으로 작업을 수행
  • 어떤 스레드에서 작업 A를 처리하다가 새로운 작업 B 가 들어오면, 그 작업 B 을 수행하고 완료될때까지 기다렸다가 다시 작업 A 를 수행하는 작업 처리 방식.
  • 비동기 = asynchronous = async→ 병렬적으로 작업을 수행
  • 어떤 스레드에서 작업 A 를 처리하다 새로운 작업 B 가 들어오면, 다른 스레드에 작업 B를 넘기고, 수행하고 있던 작업 A는 멈추지 않고 병렬적으로 동시에 수행하는 작업 처리 방식. 작업 B 가 완료되면, 완료되었다는 결과와 소식을 전해받는다.

예를 들어 어떤 스레드에서 UI를 그리는 작업을 하다가, 네트워크 통신을 해서 어떤 데이터를 받는 작업이 새로 들어왔다고 가정해 봅시다.

  • 동기적으로 작업 수행
  • UI를 그리다가 멈추고, 네트워크 통신 작업을 한 뒤 서버에서 결과가 도착했을 때 다시 UI 그리는 작업 시작.
  • 비동기적으로 작업 수행
  • UI 를 그리다가, 네트워크 통신 작업이 들어오면 다른 스레드에 작업을 넘기고 UI 작업은 이어서 계속 수행. 서버 결과가 돌아왔을 때 응답을 돌려받음.
  • 앱에서 처리해야 할 여러 가지 작업들을 모두 하나의 스레드에서 동기적으로 처리한다면, 앱의 성능이 매우 떨어지게 될 것입니다. 따라서 개발자는 반드시 동기/비동기 개념과 스레드의 개념에 대해서 이해해야 합니다. 

  ♻️  DispatchQueue, GCD 개념

▪️ GCD (Grand Central Dispath)

 

 

Dispatch | Apple Developer Documentation

Execute code concurrently on multicore hardware by submitting work to dispatch queues managed by the system.

developer.apple.com

 

애플 공식문서에서 Grand Central Dispatch (GCD)는 동시성 프로그래밍을 돕는 프레임워크라고 명시하고 있다. iOS, macOS, watchOS 등등에 사용할 수 있는 동시성 프레임 워크. 동시성 프로그래밍이란 여러 가지 일을 동시에 작업하도록 하는 프로그래밍을 의미. 그중에서 Swift에서 사용하는 GCD 기술로 Dispatch Queue 가 있다. 즉 GCD 안에 DispatchQueue 가 포함되는 개념.

 

즉, 위에서 배웠던 대로 여러 가지의 스레드를 가지고 멀티 스레딩 작업을 하며, 비동기적으로 여러 가지 작업을 수행하기 위해서는 Dispatch Queue를 사용하면 된다.

 

DispatchQueue

  • Dispatch: 보내다, 발송하다.
  • Queue: 큐. 자료구조 중 큐를 의미.

그러면 Dispatch Queue 라는건 어디론가 보내기 위한 큐를 의미하는데 이건 무슨 뜻일까? 아래 개념들을 공부하고 다시 이해해 봅시다.

 

  • 큐 (Queue)의 개념
    • 여러 가지 자료구조 중 하나로, 먼저 들어온 데이터가 먼저 나간다는 특징을 가짐. (선입선출)
    • 우선순위 큐 (Priority Queue)는 데이터가 나가는 우선순위 규칙을 부여하고, 우선순위에 따른 순서대로 데이터를 내보냄.
    • 쉽게 생각하면 데이터를 집어넣고, 내부 규칙에 따른 순서대로 데이터를 내보내는 자료구조.

→ 과거 프로그래밍에서는 멀티 스레딩, 비동기 작업을 하기 위해서는 개발자가 직접 스레드를 생성하고, 명시하고, 어떤 스레드에 작업을 할당해 줄지 스스로 판단한 뒤 넘겨줘야 해야 했습니다. 이는 복잡하고 어려우며 개발자가 할 일이 많았습니다.

 

→ 이를 해결하는 시스템이 DispatchQueue입니다. 개발자는 작업이 생겼을 때 그저 Dispatch Queue에 작업을 넘겨줍니다. 그러면 운영체제가 DispatchQueue에 들어간 작업들을 살피며 적절한 스레드에 할당하게 됩니다. 개발자의 수고가 덜어졌습니다.

 

즉 DispatchQueue는 어떤 스레드로 작업을 보내기 위한 큐 를 의미하는 것입니다.

개발자가 작업을 DispatchQueue에 보내면, 알아서 적절한 스레드를 찾아 작업을 할당시킵니다.