1) ViewController 생명주기
2) 메모리 관리 이해
3) Core Data 와 UserDefaults
4) 네트워크 통신 이해
5) 날씨 앱 만들기
여기서는 1, 2를 다룬다.
3에 관한 내용은 여기로
4는 여기로
UIViewController
: UIKit 앱의 뷰 계층을 관리
UIViewController
를 가짐UIViewController
내부에 UIView
, UIButton
, UIScrollView
등 UI 클래스들을 배치하며 화면 구성: 앱 생명주기 & ViewController
생명주기
뷰컨트롤러의 생명주기: 보여졌다 -> 사라지는 주기
: 뷰의 로딩이 완료되었을 때 자동으로 호출되며, 화면이 만들어질 때 처음 한 번만 실행된다.
따라서 일반적으로 리소스를 초기화하거나, 초기 화면을 구성하는 용도로 사용.
: 뷰가 나타날 것이라는 신호를 컨트롤러에게 알리는 역할.
예를 들어 네비게이션 뷰 안에서, 어떤 뷰가 처음 열릴 때 viewDidLoad
와 viewWillAppear
가 함께 실행된다. 하지만 하위 페이지에 잠시 갔다가 다시 돌아오는 경우, 이미 뷰가 한 번 로드되었기 때문에 viewWillAppear
메서드만 다시 실행된다.
네비게이션 뷰 안에서 상위 페이지에서 하위 페이지로 재진입할 때는 viewDidLoad가 다시 실행된다. 이는 iOS의 네비게이션 뷰의 동작이 스택으로 처리되기 때문!
참고
따라서 뷰에 다시 진입함에 따라 바꾸고 싶은 요소가 있다면 여기서 처리하면 된다.
: 뷰가 나타난 직후에 실행된다는 점 외에 viewWillAppear
과 거의 같다.
: 뷰가 사라지기 직전에 호출되는 함수로, 뷰가 삭제되려고 하는 것을 뷰컨트롤러에 알린다.
: 뷰컨트롤러에 뷰가 제거되었음을 알린다.
UserDefaults
, CoreData
등을 활용해 디스크에 데이터를 저장할 수 있다.👉🏻 앞으로 앱의 기능을 만들 때, 메모리를 활용하는게 좋을지 디스크를 활용하는게 좋을지 판단하고 사용해야 한다.
사용하지 않는 메모리가 쌓이고 쌓여 메모리에 부담을 주는 상황을 메모리 누수 (Memory Leak)이라고 한다.
자바에서는 개발자가 명시적으로 메모리 관리를 하지 않아도 기본적으로 메모리 누수를 방지하기 위한 Garbage Collector
라는 시스템이 있다.
가비지 컬렉터는 런타임에 메모리 영역을 훑으며 현재 사용중인 것에는 Mark하고, Mark되지 않은 것들을 정리하는 Mark-and-Sweep
방식으로 작동한다.
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이 되었는지 관리해 주는 것이 중요하다.
: (Swift) 위에서 언급한 JAVA의 GC처럼 자동으로 메모리 관리해주는 시스템
Reference Count
를 자동으로 계산: Obj-C에서 사용하는 메모리 관리 시스템
Reference Count
를 개발자가 코드로 직접 계산ARC로 잡아내지 못하는 Memory Leak 상황이 발생할 수 있으니 메모리 관리 방법을 반드시 알아둬야 한다.
weak
키워드 사용클로저의 캡처링과 강한 참조 방지 해당 게시물 참고
: 아래 그림과 같이 서로가 서로를 참조하는 상황을 순환 참조라고 한다. 순환 참조는 메모리 누수를 발생시키는 대표적인 사례.
예시)
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 클래스 소멸
어렵다~~ 앞으로는 메모리도 관리해 보는 개발린이가 되어야겠다...
네비게이션 뷰가 저런 식으로 돌아가는군요 신기해요 ㅋㅋ