[내배캠][Swift] 문법 심화 정리 (3)

팔랑이·2024년 6월 13일

iOS/Swift

목록 보기
31/90
post-thumbnail
Swift 문법 심화 목차

1. 프로퍼티 옵저버
2. 타입 캐스팅
3. 접근제한자
4. 클로저
5. 고차함수
6. 예외처리
7. ARC와 메모리 누수
8. 프로토콜
9. 확장
10. 제네릭
11. 비동기와 네트워킹
12. Combine 맛보기
13. RxSwift 맛보기

Swift 문법 심화 정리 (1) 은 여기로: 1. 프로퍼티 옵저버 / 2. 타입 캐스팅 / 3. 접근 제한자

Swift 문법 심화 정리 (2) 는 여기로: 4. 클로저 / 5. 고차함수 / 10. 제네릭

오늘은 예외처리, ARC와 메모리 누수, 비동기와 네트워킹을 다뤄 볼 예정


6. 예외처리

예외처리에 대한 자세한 내용은 여기

7. ARC와 메모리 누수

ARC는 스위프트의 메모리 관리 기법으로, 개발자가 객체의 메모리를 명시적으로 관리하지 않아도 되도록 한다. 하지만 잘못된 참조 관계를 만들면 메모리 누수가 발생할 수 있다.

자동 참조 카운팅(ARC)

스위프트는 객체의 메모리를 자동으로 관리하기 위해 ARC(Automatic Reference Counting)를 사용한다. ARC는 각 객체가 얼마나 많은 참조를 받고 있는지 카운트하고, 더 이상 참조되지 않는 객체를 자동으로 해제한다.

참조 타입

스위프트에서 클래스는 참조 타입으로, 클래스 인스턴스는 여러 변수나 상수에서 참조될 수 있다. ARC는 이러한 참조를 카운트해서, 객체가 더 이상 사용되지 않을 때 메모리를 해제한다.

❗️ 참고: 열거형과 구조체는 값 타입이다.

강한 참조, 약한 참조, 미소유 참조

  • 강한 참조(Strong Reference): 기본 참조 방식으로, 객체의 참조 카운트를 증가시켜 객체가 메모리에서 해제되지 않도록 한다.
  • 약한 참조(Weak Reference): 참조 카운트를 증가시키지 않아 객체가 메모리에서 해제될 수 있다. 옵셔널로 선언되며, 객체가 해제되면 자동으로 nil이 된다.
  • 미소유 참조(Unowned Reference): 참조 카운트를 증가시키지 않지만, 객체가 해제되지 않을 것이라는 가정하에 사용된다. 옵셔널이 아니며, 객체가 해제되면 런타임 에러가 발생할 수 있다.

메모리 누수

메모리 누수는 객체가 더 이상 필요 없지만, 메모리에서 해제되지 않고 남아있는 상황을 의미한다. 주로 강한 참조 순환(Strong Reference Cycle) 때문에 발생.

강한 참조 순환

두 객체가 서로를 강하게 참조하면, 서로를 해제하지 못하는 상황이 발생하는데 이를 강한 참조 순환이라고 한다.

class Person {
    var name: String
    var apartment: Apartment?
    
    init(name: String) {
        self.name = name
    }
    
    deinit {
        print("\(name) is being deinitialized")
    }
}

class Apartment {
    var unit: String
    var tenant: Person?
    
    init(unit: String) {
        self.unit = unit
    }
    
    deinit {
        print("Apartment \(unit) is being deinitialized")
    }
}

var john: Person? = Person(name: "John Appleseed")
var unit4A: Apartment? = Apartment(unit: "4A")

john?.apartment = unit4A
unit4A?.tenant = john

john = nil
unit4A = nil

// 여기서 Person과 Apartment 인스턴스는 메모리에서 해제되지 않음

약한 참조와 미소유 참조로 강한 참조 순환 해결

약한 참조

class Person {
    var name: String
    var apartment: Apartment?
    
    init(name: String) {
        self.name = name
    }
    
    deinit {
        print("\(name) is being deinitialized")
    }
}

class Apartment {
    var unit: String
    weak var tenant: Person?
    
    init(unit: String) {
        self.unit = unit
    }
    
    deinit {
        print("Apartment \(unit) is being deinitialized")
    }
}

var john: Person? = Person(name: "John Appleseed")
var unit4A: Apartment? = Apartment(unit: "4A")

john?.apartment = unit4A
unit4A?.tenant = john

john = nil
unit4A = nil

// 이제 Person과 Apartment 인스턴스는 메모리에서 해제됨

미소유 참조

class Customer {
    var name: String
    var card: CreditCard?
    
    init(name: String) {
        self.name = name
    }
    
    deinit {
        print("\(name) is being deinitialized")
    }
}

class CreditCard {
    var number: String
    unowned var customer: Customer
    
    init(number: String, customer: Customer) {
        self.number = number
        self.customer = customer
    }
    
    deinit {
        print("Card #\(number) is being deinitialized")
    }
}

var john: Customer? = Customer(name: "John Appleseed")
john?.card = CreditCard(number: "1234-5678-9012-3456", customer: john!)

john = nil

// 이제 Customer와 CreditCard 인스턴스는 메모리에서 해제됨

결론

ARC는 스위프트에서 객체의 메모리를 자동으로 관리해주지만, 강한 참조 순환을 방지하기 위해 약한 참조와 미소유 참조를 적절히 사용해야 한다. 이렇게 하면 메모리 누수를 방지하고, 메모리를 효율적으로 관리할 수 있다.


11. 비동기와 네트워킹

1. 스레드

: 프로그램의 작업을 병렬로 수행하기 위한 기본 단위이다. 스레드를 사용하면 동시에 여러 작업을 처리할 수 있다. 스레드는 프로세스 내에서 실행되며, 프로세스의 자원을 공유한다. 여러 스레드를 사용하면 CPU 코어를 효율적으로 활용할 수 있지만, 스레드 간의 자원 공유와 동기화 문제로 인해 복잡성이 증가할 수 있다.

Swift의 스레드 종류

메인 스레드(Main Thread) - 애플리케이션의 주요 인터페이스 및 UI 업데이트를 담당하는 스레드입니다. UI 요소의 변경은 메인 스레드에서 수행되어야 합니다.
백그라운드 스레드(Background Threads) - 메인 스레드 이외에 동시에 작업을 수행하기 위해 생성되는 스레드들을 일컫습니다. 주로 작업을 분산하거나 병렬로 처리할 때 사용됩니다.

2. 동기와 비동기

  • 동기 (Synchronoun): 요청과 결과가 순차적으로 발생하며, 호출한 작업이 완료될 때까지 다음 작업을 기다린다. 동기 작업은 코드의 흐름이 직관적이지만, 긴 작업을 실행할 때 메인 스레드를 차단할 수 있다.

  • 비동기 (Asynchronous): 비동기 작업은 요청과 결과가 독립적으로 발생하며, 호출한 작업이 완료되기 전에 다음 작업을 계속 진행할 수 있다. 비동기 작업은 UI 응답성을 유지하는 데 유리하지만, 콜백이나 클로저를 사용하여 작업 완료를 처리해야 하므로 코드가 복잡해질 수 있다.

3. 직렬과 동시

  • 직렬 큐(Serial Queue): 한 번에 하나의 작업만 순서대로 처리한다. 작업이 큐에 추가된 순서대로 실행되며, 이전 작업이 완료된 후에야 다음 작업이 시작된다. 직렬 큐는 데이터 무결성을 보장하며, 동시에 실행되면 문제가 발생할 수 있는 작업에 적합하다.

  • 동시 큐(Concurrent Queue): 여러 작업을 동시에 처리할 수 있다. 작업이 큐에 추가된 순서대로 시작되지만, 동시에 실행될 수 있다. 동시 큐는 작업을 병렬로 처리하여 성능을 높일 수 있지만, 동시 실행으로 인해 발생할 수 있는 동기화 문제를 주의해야 한다.

4. GCD

GCD(Grand Central Dispatch)는 멀티스레딩을 쉽게 관리하기 위한 저수준 API이다. GCD를 사용하면 비동기 작업을 간편하게 관리할 수 있다. DispatchQueue를 사용하여 작업을 직렬 또는 동시로 실행할 수 있다. GCD는 시스템 수준에서 최적화되어 있어 성능이 뛰어나다.

5. DispatchQueue

  • DispatchQueue.main은 메인 스레드에서 실행되는 직렬 큐이다. 주로 UI 업데이트 작업에 사용된다. 메인 스레드는 앱의 UI를 담당하므로, 메인 큐에서 긴 작업을 실행하면 UI가 응답하지 않게 될 수 있다. 따라서 메인 큐에서는 짧고 빠른 작업만 실행하는 것이 좋다.

  • DispatchQueue.global은 시스템에서 제공하는 동시 큐이다. 여러 가지 QoS(Quality of Service) 레벨로 제공되며, 백그라운드 작업, 네트워크 호출 등 다양한 용도로 사용된다. DispatchQueue.global(qos: .background)와 같이 특정 QoS를 지정하여 작업의 우선순위를 조절할 수 있다.

예제:

DispatchQueue.global(qos: .background).async {
    // 백그라운드에서 실행될 작업
    print("Performing task in background")
    
    DispatchQueue.main.async {
        // 메인 스레드에서 UI 업데이트
        print("Updating UI on main thread")
    }
}

6. 네트워킹

네트워킹은 인터넷이나 로컬 네트워크를 통해 데이터 통신을 하는 것이다. 스위프트에서는 주로 URLSession을 사용하여 네트워크 요청을 처리한다. 데이터를 가져오거나, 보내거나, 파일을 다운로드하고 업로드할 수 있다. 네트워크 요청은 비동기적으로 처리하여 UI 응답성을 유지할 수 있다.

예제:

import Foundation

func fetchData(from url: URL) {
    let task = URLSession.shared.dataTask(with: url) { data, response, error in
        // 에러 처리
        if let error = error {
            print("Error: \(error)")
            return
        }

        // 데이터 확인
        guard let data = data else {
            print("No data received")
            return
        }

        // 데이터 처리 (예: JSON 파싱)
        do {
            let json = try JSONSerialization.jsonObject(with: data, options: [])
            print("JSON response: \(json)")
        } catch {
            print("Failed to parse JSON: \(error)")
        }
    }
    task.resume()
}

if let url = URL(string: "https://api.example.com/data") {
    fetchData(from: url)
}
profile
정체되지 않는 성장

0개의 댓글