[iOS][Swift] iOS 숙련 강의 - 1

팔랑이·2024년 7월 9일
1

iOS/Swift

목록 보기
42/71

iOS 숙련강의 목차

1) ViewController 생명주기
2) 메모리 관리 이해
3) Core Data 와 UserDefaults
4) 네트워크 통신 이해
5) 날씨 앱 만들기

여기서는 1, 2를 다룬다.
3에 관한 내용은 여기
4는 여기


2-1. ViewController 생명주기

UIViewController

UIViewController : UIKit 앱의 뷰 계층을 관리

  • 한 개의 페이지는 반드시 하나의 UIViewController를 가짐
  • UIViewController 내부에 UIView, UIButton, UIScrollView 등 UI 클래스들을 배치하며 화면 구성

ViewController의 생명주기

iOS의 대표적인 생명주기 2가지

: 앱 생명주기 & ViewController 생명주기

ViewController LifeCycle

뷰컨트롤러의 생명주기: 보여졌다 -> 사라지는 주기

viewDidLoad()

: 뷰의 로딩이 완료되었을 때 자동으로 호출되며, 화면이 만들어질 때 처음 한 번만 실행된다.
따라서 일반적으로 리소스를 초기화하거나, 초기 화면을 구성하는 용도로 사용.

viewWillAppear()

: 뷰가 나타날 것이라는 신호를 컨트롤러에게 알리는 역할.

예를 들어 네비게이션 뷰 안에서, 어떤 뷰가 처음 열릴 때 viewDidLoadviewWillAppear가 함께 실행된다. 하지만 하위 페이지에 잠시 갔다가 다시 돌아오는 경우, 이미 뷰가 한 번 로드되었기 때문에 viewWillAppear 메서드만 다시 실행된다.

네비게이션 뷰 안에서 상위 페이지에서 하위 페이지로 재진입할 때는 viewDidLoad가 다시 실행된다. 이는 iOS의 네비게이션 뷰의 동작이 스택으로 처리되기 때문!
참고

따라서 뷰에 다시 진입함에 따라 바꾸고 싶은 요소가 있다면 여기서 처리하면 된다.

viewDidAppear()

: 뷰가 나타난 직후에 실행된다는 점 외에 viewWillAppear과 거의 같다.

viewWillDisappear()

: 뷰가 사라지기 직전에 호출되는 함수로, 뷰가 삭제되려고 하는 것을 뷰컨트롤러에 알린다.

viewDidDisappear()

: 뷰컨트롤러에 뷰가 제거되었음을 알린다.


2-2. 메모리 관리 이해

메모리 vs 디스크

  • 메모리: 주로 휘발성 메모리인 RAM을 말한다. EEPROM과 같은 비휘발성 메모리도 있으며, 아이폰은 이곳에 장치의 일련번호 및 하드웨어 정보를 저장한다.
  • 디스크: 영구적인 데이터를 저장하는 비휘발성 장치를 말하며, UserDefaults, CoreData 등을 활용해 디스크에 데이터를 저장할 수 있다.

👉🏻 앞으로 앱의 기능을 만들 때, 메모리를 활용하는게 좋을지 디스크를 활용하는게 좋을지 판단하고 사용해야 한다.

Garbage Collector

사용하지 않는 메모리가 쌓이고 쌓여 메모리에 부담을 주는 상황을 메모리 누수 (Memory Leak)이라고 한다.

자바에서는 개발자가 명시적으로 메모리 관리를 하지 않아도 기본적으로 메모리 누수를 방지하기 위한 Garbage Collector라는 시스템이 있다.

가비지 컬렉터는 런타임에 메모리 영역을 훑으며 현재 사용중인 것에는 Mark하고, Mark되지 않은 것들을 정리하는 Mark-and-Sweep 방식으로 작동한다.

Reference Counting

Swift의 메모리 관리 시스템의 핵심이 되는 개념이다.

class MyClass {}
let myclass = MyClass()

위와 같이 인스턴스를 생성하면 메모리가 할당되는데, 이 인스턴스는 하나 이상의 참조자(소유자) 가 있어야 메모리에 유지된다.이 인스턴스를 참조하는 소유자의 개수를 reference count 라고 하는데, reference count가 0이 되면 메모리에서 삭제된다.

예를 들어 다음 코드를 보면,

class MyClass {
    init() {
        print("MyClass 생성")
    }
    deinit {
        print("MyClass 소멸")
    }
}

// RC = 1
var myClass: MyClass? = MyClass()

// RC = 2
var myClass2 = myClass

// RC = 2-1 = 1
myClass = nil

myClass2가 여전히 MyClass를 참조하고 있기 때문에 MyClass의 참조 카운트는 1이 되어, deinit의 print가 호출되지 않는다.

따라서 객체를 더 이상 사용하지 않을 경우, reference count가 0이 되었는지 관리해 주는 것이 중요하다.

ARC와 MRC

ARC (Automatic Reference Counting)

: (Swift) 위에서 언급한 JAVA의 GC처럼 자동으로 메모리 관리해주는 시스템

  • Reference Count를 자동으로 계산
  • 객체 생성시 RC가 1로 설정
  • 객체가 다른 변수나 속성에 할당되어 참조될 때마다 1씩 증가 / 해제될 때마다 1씩 감소
  • RC가 0이 되면 메모리에서 해제

MRC (Manual Reference Counting)

: Obj-C에서 사용하는 메모리 관리 시스템

  • Reference Count를 개발자가 코드로 직접 계산
  • 객체 생성시 개발자가 명시적으로 메모리 할당
  • 객체가 다른 변수나 속성에 할당되어 참조될 때마다 1씩 명시적으로 RC증가 / 해제될 때마다 1씩 감소
  • RC가 0이 되면 명시적으로 메모리에서 해제

ARC로 잡아내지 못하는 Memory Leak 상황이 발생할 수 있으니 메모리 관리 방법을 반드시 알아둬야 한다.

약한 참조와 강한 참조

  • 약참조: RC를 증가시키지 않으면서 참조하는 것, weak 키워드 사용
  • 강참조: RC 증가시키며 참조, 일반적인 참조

강한 참조 방지

클로저의 캡처링과 강한 참조 방지 해당 게시물 참고

순환 참조

: 아래 그림과 같이 서로가 서로를 참조하는 상황을 순환 참조라고 한다. 순환 참조는 메모리 누수를 발생시키는 대표적인 사례.

예시)

class Person {
    var pet: Dog?
    init() {
        print("Person 클래스 생성")
    }
    deinit {
        print("Person 클래스 소멸")
    }
}

class Dog {
    var owner: Person?
    init() {
        print("Dog 클래스 생성")
    }
    deinit {
        print("Dog 클래스 소멸")
    }
}

// person rc = 1
var person: Person? = Person()
// dog rc = 1
var dog: Dog? = Dog()

// dog rc = 2
person?.pet = dog
// person rc = 2
dog?.owner = person

// person rc = 1
person = nil
// dog rc = 1
dog = nil

위 예시에서는 서로를 강하게 참조하고 있기 때문에 객체를 nil로 선언하더라도 RC가 1로 남아 메모리에서 해제되지 않는다.

이를 다음과 같이 Person 클래스의 Dog에 대한 참조를 강한 참조로 유지하고, Dog클래스에서 Person에 대한 참조를 약한 참조로 변경하면 순환 참조 문제를 해결할 수 있다.

class Person {
    var pet: Dog?
    init() {
        print("Person 클래스 생성")
    }
    deinit {
        print("Person 클래스 소멸")
    }
}

class Dog {
    weak var owner: Person?
    init() {
        print("Dog 클래스 생성")
    }
    deinit {
        print("Dog 클래스 소멸")
    }
}

// person rc = 1
var person: Person? = Person()
// dog rc = 1
var dog: Dog? = Dog()

// dog rc = 2
person?.pet = dog
// person rc = 1 
dog?.owner = person

// person rc = 0, dog rc = 1
person = nil
// dog rc = 0
dog = nil

// Output:
// Person 클래스 생성
// Dog 클래스 생성
// Person 클래스 소멸
// Dog 클래스 소멸

어렵다~~ 앞으로는 메모리도 관리해 보는 개발린이가 되어야겠다...

profile
정체되지 않는 성장

1개의 댓글

comment-user-thumbnail
2024년 7월 11일

네비게이션 뷰가 저런 식으로 돌아가는군요 신기해요 ㅋㅋ

답글 달기