ARC

zoe·2021년 11월 22일
1

Swift

목록 보기
4/4

✔ ARC

  • 자동으로 메모리를 관리해주는 친구
  • 객체에 대한 참조 카운트를 관리하고 0이 되면 자동으로 메모리를 해제한다
  • compile time에 실헹한다(run time에 계속 실행되는게 아님)
  • retain cycle에 유의해야 함

✔ retain, release

retain reference count 증가를 통해 현재 scope에서 객체가 유지되는 것을 보장한다
release reference count를 감소시킨다. retain 후에 필요없을 때 reslease를 한다.

✔ compile time에 실행되는데 어떻게 동적으로 실행되는 것들의 reference count를 세고 메모리 관리를 할 수 있는가?


complie time에 자동으로 retain, release를 적절한 위치에 삽입하는 방식으로 메모리를 관리한다.
이렇게 자동으로 retain, release를 삽입해서 관리를 하고 0이될 때 deinit을 호출해서 메모리를 해제시킨다.

✔ ARC를 이해해야 하는 이유?

  • 메모리 관리는 Data, Heap, Stack, Code 이렇게 4가지 가상 메모리 영역중 Heap영역과 관련되어 있다

  • Heap은 Class, closure 등 참조형 자료들이 머무는 공간이자, 개발자가 동적으로 할당하는 메모리 공간이기 때문에 관리가 필요하다

  • 관리를 하기 위해서 Heap영역에 참조형 자료들이 얼마나 참조되고 있는지 카운팅하고 이에따라 메모리를 항당 및 제거하면 된다

  • 그리고 이것을 자동으로 해주는 것이 바로 ARC이다

  • ARC가 해주는건 retain과 releae를 자동으로 삽입해주는것 밖에 없다. 자동으로 삽입하는 방법을 우리가 조금 조절하고 싶을 때

  • 우리가 원하는 방향으로 메모리 관리가 이루어지려면 ARC 에 명확한 힌트를 주어야 하기 때문이다

우리가 원하는 방향이 아닌 경우는 언제일까?

  • 클래스의 인스턴스에서 강한 참조 싸이클 문제가 발생할 때
  • 클로저에서 강한 참조 싸이클 문제가 발생할 때

✔ 참조횟수가 (+)되는 경우

  1. 인스턴스를 새로 생성할 때
  2. 기존 인스턴스를 다른 변수에 대입할 때

✔ 참조횟수가 (-)되는 경우

  1. 인스턴스를 가리키던 변수가 메모리에서 해제됐을 때
func makeClone(_ origin: Human) {
    let clone = origin                          // ② RC : 2
}
 
let sodeul = Human(name: "Sodeul", age: 26)     // ① RC : 1
makeClone(sodeul) // ③ RC : 1 함수가 종료되어 지역변수 clone이 스택에서 해제되는 순간 RC -1이 된다
  1. nil이 지정되었을 때
  2. 변수에 다른 값을 대입한 경우
var zoe = Human(name: "zoe", age: 24) // zoe의 RC : 1
var allie = Human(name: "allie", age: 22) // allie의 RC : 1

zoe = allie // zoe의 RC : 0, allie의 RC : 2
zoe에 allie의 값을 대입하게 되면 zoe의 RC-1, allie의 RC+1
zoe 변수에 저장된 주소값이 바뀌었으니 당연히 참조 카운트도 변하게 된다
zoe가 가리키던 인스턴스의 RC0이 되었으므로 메모리에서 해제된다
  1. 프로퍼티의 경우, 속해있는 클래스의 인스턴스가 메모리에서 해제될 때

✔ 구조체와 열거형은 왜 메모리 관리를 안해주는가?

  • 값타입이기 때문이다

✔ 순환참조란?

  • 순환참조 발생시 영구적으로 메모리가 해제되지 않을 수 있다.
  • strong으로 선언된 변수들이 순환참조 됐을 시 서로가 서로를 참조하고 있어서 RC가 0이 되지 못한다
  • 심지어 해당 인스턴스를 가리키던 변수도 nil이 되었기 때문에 인스턴스에 접근할 수 있는 방법도 없어 메모리 해제도 못하게 된다
  • 즉 어플이 죽기 전까진 memory leak이 계속 발생하는 것이다
    이렇게 strong을 사용해서 순환참조에 문제가 생긴 경우를 강한 순환참조 라고 한다

✔ weak

  • 인스턴스를 참조할 때 RC를 증가시키지 않는다
  • 참조하던 인스턴스가 메모리에서 해제된 경우 , 자동으로 nil이 할당되어 메모리가 해제된다
  • 프로퍼티를 선언한 이루 나중에 nil이 할당된다는 관점으로 보아 weak는 옵셔널 타입의 변수여야 한다

✔ weak는 어느쪽에 붙이면 좋을까?

  • 수명이 동일하다면 둘 중 한 쪽을 weak로 선언해 주지만
  • 수명이 다를경우 수명이 더 짧은 인스턴스를 가리키는 애를 약한 참조로 선언해야 한다
    영희가 먼저 죽는다 -> 철수의 girlFriend가 nil이 될 수 있다 -> 철수의 girlFriend를 weak으로 선언한다

✔ 언제 strong을 써야 하는가?

  • Strong은 소유대상의 reference count를 1 증가시킴으로써 dealloc 되지 않도록 하고, Weak 는 reference count는 증가시키지 않고 소유함으로써 상호참조 발생을 막아 메모리 누수를 막기위해 사용한다
  • 복잡한 뷰 Hierarchy일때 weak대신 strong을 써야하는 경우도 존재한다

✔ lldb에서 RC를 알려주는 명령어

po CFGetRetainCount(self)

Method dispatch가 무엇인가? 읽어보기

final class 가 class 보다 성능이 좋다
일반 클래스는 Method Dispatch가 Dynamic 인데, 파이널 클래스는 Static이기 때문이다
https://sihyungyou.github.io/iOS-method-dispatch/

Writing High-Performance Swift Code

https://github.com/apple/swift/blob/main/docs/OptimizationTips.rst#advice-use-final-when-you-know-the-declaration-does-not-need-to-be-overridden

참고한 블로그
https://sujinnaljin.medium.com/ios-arc-뿌시기-9b3e5dc23814
https://babbab2.tistory.com/26?category=831129

profile
개발하면서 마주친 문제들을 정리하는 공간입니다.

0개의 댓글