Taehyeon-Kim/SeSAC

[220905] TIL

Opened this issue · 2 comments

ARC

메모리 구조

  • 코드 : 우리가 작성하는 코드가 들어가는 영역
  • 데이터 : 전역적으로 쓰일 수 있는 변수 (타입 프로퍼티)
  • 힙 : ARC가 관리하는 영역, 클래스의 경우 인스턴스를 생성(인스턴스를 생성, 제거할 때마다 메모리에 올렸다가 내렸다가 하는 과정 필요)
  • 스택

ARC

  • Auto Reference Counting

RC (참조 횟수)

  • Reference Count
  • 인스턴스 생성 -> 메모리에 올라갔다. -> RC+1
  • 인스턴스 해제 -> 메모리에서 내려갔다. -> RC-1
  • RC == 0 인 시점에 힙영역에서 내린다. (=메모리 해제)

생성/해제

var user: User? = User(name: "고래밥") // User: RC 1
var guild: Guild? = Guild(name: "SeSAC") // Guild: RC 1

guild = nil // Guild: RC 0
user = nil // User: RC 0

/*
User init
Guild init
Guild deinit
User deinit
*/
var user: User? = User(name: "고래밥") // User: RC 1
var guild: Guild? = Guild(name: "SeSAC") // Guild: RC 1

user?.guild = guild // Guild: RC 2
guild?.owner = user // User: RC 2

guild = nil // Guild: RC 1
user = nil // User: RC 1

/*
User init
Guild init
*/

// ==> 순환 참조 발생

해결

순환 참조인 녀석을 먼저 nil

var user: User? = User(name: "고래밥") // User: RC 1
var guild: Guild? = Guild(name: "SeSAC") // Guild: RC 1

user?.guild = guild // Guild: RC 2
guild?.owner = user // User: RC 2

guild?.owner = nil // User: RC 1

guild = nil // Guild: RC 1
user = nil // User: RC 0 -> user deinit -> Guild: RC 0

/*
User init
Guild init
User deinit
Guild deinit
*/

수동으로 해결하기 어렵다.

  • 관계 다 찾아서 해결할 수 있겠니?
  • 아뇨
  • 그래서 쉽게 해결할 수 있는 방법을 준비했단다.

weak, unowned keyword

  • weak : RC를 증가시키지 않는다.
    • ex. IBOutlet 부분
    • 수명(RC로 파악)이 더 짧은 인스턴스를 가리키는 녀석을 약한 참조(weak)로 선언하자
  • unowned : RC를 증가시키지 않는다.

ARC + delegate

  • delegate를 사용할때는 weak를 필수적으로 써주어야 함.
  • 뷰 컨트롤러가 생성되고 해제되는 문제를 해결해주어야 함.
  • weak 키워드를 경우에는 결국 class에서 사용을 제한해주어야 하기 때문에 AnyObject로 제약을 걸어주어야 함.
protocol MyDelegate: AnyObject {
    func sendData(_ data: String)
}
protocol BDelegate: AnyObject {
  func method()
}

class A: BDelegate {

  lazy var b: B = {
    let view = B()
    view.delegate = self
    return view
  }()

  func method() {
    // code
  }

}

class B {

  weak var delegate: BDelegate?

  func dismiss() {
    delegate?.method()
  }

}

// var A: A? = A()
// A?.b
// A.nil

/*
weak키워드로 참조를 약하게 만들지 않으면 A, B의 인스턴스는 해제되지 않는다.
*/

ARC + Closure

import UIKit

class User {
    var nickname = "JACK"
    
    lazy var introduce: () -> String = { [weak self] in
        return "저는 \(String(describing: self?.nickname))입니다."    // self로 접근하기 때문에 순환 참조 발생
    }

    init() {
        print("User init")
    }
    
    deinit {
        print("User deinit")
    }
}

var user: User? = User()
user?.introduce()       // RC + 1
user = nil

// User init
// User deinit

값 캡처

  • 호출시에 값이 캡처된다.
func myClosure() {
    
    var number = 0
    print("1: \(number)")
    
    let closure: () -> Void = { [number] in  // 값 캡처, 값 타입 - 복사, 클로저 안에서는 값이 영향을 받지 않는다.
        print("closure: \(number)")
    }
    
    closure()
    
    number = 100
    print("2: \(number)")
    
    closure()
}

myClosure()

/*
1: 0
closure: 0
2: 100
closure: 0
*/

Swift에서 메모리를 어떻게 하죠?

  • MRC -> ARC
  • 참조에 대한 차이로 strong, weak, unowned가 존재한다.

weak와 unowned의 차이는 무엇인가?

메모리에서 인스턴스가 해제되었는지 어떻게 아나요?

  • deinit으로 확인해본다.