ARC, Automatic Reference Counting ๐Ÿฆฉ

์ธ์ƒ๋…ธ์žผ์‹œ๊ธฐยท2021๋…„ 3์›” 9์ผ
0

๐Ÿฆ… ์Šค์œ„ํ”„ํŠธ ๋ฌธ๋ฒ•

๋ชฉ๋ก ๋ณด๊ธฐ
2/13

https://baked-corn.tistory.com/30
https://velog.io/@msi753/ARC

ARC ์‹œ์Šคํ…œ

์ฐธ์กฐํ•˜๋Š” ๋ณ€์ˆ˜๋ฅผ ์นด์šดํŠธํ•˜๋Š” ์‹œ์Šคํ…œ
์•„์›ƒ๋ › ๋ณ€์ˆ˜๊ฐ€ weakํƒ€์ž…์„ ๊ธฐ๋ณธ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ๋Š”

    1. ๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ
    • weak ํƒ€์ž…์ผ ๋•Œ, ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๋ถ€์กฑํ•˜๋ฉด ViewController์˜ didReceiveMemoryWarning() ๋ฉ”์†Œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด์„œ ๋ฉ”์ธ๋ทฐ๊ฐ€ nil์ด ๋˜๋„๋ก ์ฒ˜๋ฆฌ๊ฐ€ ๋˜๊ณ , weak ํƒ€์ž…์œผ๋กœ ์„ ์–ธ๋œ ๋ชจ๋“  ๋ณ€์ˆ˜๋Š” ์นด์šดํŠธ๊ฐ€ 0์ด ๋˜๋ฉด์„œ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ํ•ด์ œ๋œ๋‹ค.
    • strong ํƒ€์ž…์ผ ๋•Œ, nil ์ฒ˜๋ฆฌ๊ฐ€ ๋˜๋”๋ผ๋„ ์นด์šดํŠธ๊ฐ€ 0์ด ์•„๋‹ˆ๋ฉด ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ํ•ด์ œ๋˜์ง€ ์•Š๊ณ  ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.
    1. ์ˆœํ™˜ ์ฐธ์กฐ ์˜ค๋ฅ˜
      ์„œ๋กœ Strong์œผ๋กœ ๊ฐ•ํ•œ ์ฐธ์กฐ๋ฅผ ํ•˜๊ณ  ์žˆ์œผ๋ฉด A๋ฅผ ์ œ๊ฑฐํ•˜๋ ค๋ฉด B๋ฅผ ๋จผ์ € ์ œ๊ฑฐํ•ด์•ผ ํ•˜๊ณ , B๋ฅผ ์ œ๊ฑฐํ•˜๋ ค๋ฉด A๋ฅผ ๋จผ์ € ์ œ๊ฑฐํ•ด์•ผํ•˜๋Š” ๋…ผ๋ฆฌ์  ๋ชจ์ˆœ์ด ๋ฐœ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

Strong & Weak

strong๊ณผ weak๋Š” ์–ด๋–ป๊ฒŒ ๋‹ค๋ฅธ๊ฐ€์š”? ๐ŸŽˆ
ํ•œ ์•„์ด๊ฐ€ ํ’์„ ์„ ๋“ค๊ณ ์žˆ๋Š” ์ƒํ™ฉ์„ ๊ฐ€์ •ํ•ด๋ณด์ž.
์•„์ด๊ฐ€ ํ’์„ ์„ ๋“ค๊ณ  ์žˆ๋Š” ๊ฒƒ์ด strong,
ํ’์„ ๊ณผ ์•„์ด์˜ ์—ฐ๊ฒฐ๊ด€๊ณ„๊ฐ€ weak์ด๋‹ค.

Strong

๊ธฐ๋ณธ์ ์ธ ํด๋ž˜์Šค(reference type)๋Š” ๊ฐ•ํ•œ ์ฐธ์กฐ์ด๋‹ค.
๋ถ€๋ชจ ๊ฐ์ฒด๊ฐ€ ์ž์‹ ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ•˜๋ฉด ์นด์šดํŠธ๊ฐ€ 1์ด ์ฆ๊ฐ€ํ•˜๊ณ , ์ž์‹ ๊ฐ์ฒด๊ฐ€ ๋ถ€๋ชจ ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ•˜๋ฉด ์นด์šดํŠธ๊ฐ€ 1์ด ์ฆ๊ฐ€ํ•œ๋‹ค.
์ˆœํ™˜์ฐธ์กฐ๋ฅผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ๋ฐ–์— ์—†๋‹ค.

์˜ˆ์ œ

class TestClass{
    var testClass: TestClass? = nil
    init(){
        print("init")
    }
    deinit(){
        print("deinit")
    }
}

var testClass1: TestClass? = TestClass()
var testClass2: TestClass? = TestClass()

testClass1?.testClass = testClass2
testClass2?.testClass = testClass1

testClass1 = nil
testClass2 = nil

>> init
>> init

deinitํ•˜์ง€ ์•Š์Œ

๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ ๋ถˆ๊ฐ€๋Šฅ(๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜)

weak

๋ถ€๋ชจ๊ฐ€ ์ž์‹์„ ๊ฐ•ํ•˜๊ฒŒ ์ฐธ์กฐํ•˜๋ฉด ์นด์šดํŠธ๊ฐ€ 1 ์ฆ๊ฐ€ํ•˜์ง€๋งŒ, ์ž์‹์€ ๋ถ€๋ชจ๋ฅผ ์•ฝํ•˜๊ฒŒ ์ฐธ์กฐํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์นด์šดํŠธ๊ฐ€ ์ฆ๊ฐ€ํ•˜์ง€ ์•Š๋Š”๋‹ค

์˜ˆ์ œ

class TestClass{
    weak var testClass: TestClass? = nil  // ์ด์ œ ์ด ์ฐธ์กฐ๋Š” ์•ฝํ•œ ์ฐธ์กฐ์ด๋‹ค!
    init(){
        print("init")
    }
    deinit(){
        print("deinit")
    }
}

var testClass1: TestClass? = TestClass()
var testClass2: TestClass? = TestClass()

testClass1?.testClass = testClass2
testClass2?.testClass = testClass1

testClass1 = nil
testClass2 = nil

>> init
>> init
>> deinit
>> deinit


๊ฐ์ฒด์˜ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ํ•ด์ œ๋œ ํ›„ ๊ทธ์— ๋Œ€์‘ํ•˜๋Š” ๋ณ€์ˆ˜๋Š” ์ž๋™์œผ๋กœ nil์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.
optional ํƒ€์ž…๋งŒ์ด nil๊ฐ’์ด ๋  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ชจ๋“  weak ์ฐธ์กฐ ๋ณ€์ˆ˜๋Š” ๋ฐ˜๋“œ์‹œ optional ํƒ€์ž…์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

[weak self] vs unowned

[weak self]

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        let alert = UIAlertController(title: "Title", message: "Message", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "Done", style: .cancel, handler: { [weak self] _ in
            self?.doSomething()
        }))
        present(alert, animated: true)
    }
    
    private func doSomething() {
        print("doSomething")
    }
}
  • ๋ฃจํ”„ ๋ฐœ์ƒ(์ˆœํ™˜์ฐธ์กฐ ์˜ค๋ฅ˜)๋กœ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค
    ํด๋กœ์ € ์•ˆ์— self๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด scope's life ๋™์•ˆ self๋ฅผ strong referenceํ•˜๊ฒŒ๋ฉ๋‹ˆ๋‹ค.
    ViewController์œ„์— UIAlertController๋ฅผ ์˜ฌ๋ฆฌ๊ณ  ( ๊ฐ•ํ•œ์ฐธ์กฐ: ViewController -> UIAlertController),
    UIAlertController๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด self.doSomething์„ ์‹คํ–‰์‹œํ‚ค๋Š”๋ฐ self๋Š” ViewController๋ฅผ ๊ฐ€๋ฆฌํ‚จ๋‹ค ( ๊ฐ•ํ•œ์ฐธ์กฐ: UIAlertController -> ViewController)

  • [weak self]๋กœ ์ˆœํ™˜ ์ฐธ์กฐ ๋ฃจํ”„๋ฅผ ์—†์•ค๋‹ค
    (UIAlertController -> ViewController)๋ฅผ ์˜ต์…”๋„๋กœ ์•ฝํ•œ ์ฐธ์กฐ๋กœ ๋งŒ๋“ค์–ด ๋ฃจํ”„๋ฅผ ์ œ๊ฑฐํ•œ๋‹ค

    [weak self] _ in     // [weak self]
    self?.doSomething()  // ์˜ต์…”๋„๋กœ ๋ณ€๊ฒฝ
  • ํด๋กœ์ €์—์„œ [weak self]๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ,
    self๋ฅผ ์˜ต์…”๋„๋กœ ๋งŒ๋“ค๊ธฐ ๋•Œ๋ฌธ์—, ์œ„์ฒ˜๋Ÿผ self?.data = data์ฒ˜๋Ÿผ ์“ธ ์ˆ˜๋„ ์žˆ์ง€๋งŒ,
    guard let์„ ์‚ฌ์šฉํ•ด ์ผ์‹œ์ ์œผ๋กœ ํด๋กœ์ €์˜ ์‹œ์ž‘์— self๋ฅผ strong referenceํ•ด์ค„ ์ˆ˜๋„ ์žˆ๋‹ค.

    private var data: Data?
    
    private func getData(string: String, completion: ((Data?)->Void)) {
        completion(nil)
    }
    
    func foo() {
        getData(string: "") { [weak self] data in
            guard let strongSelf = self else {
                return
            }
            strongSelf.data = data
            // ์•„๋‹ˆ๋ฉด self?.data = data๋กœ ์ผ์„ ๊ฑฐ๋‹ค
        }
    }
  • delegate ํŒจํ„ด์—์„œ weak๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ
protocol SomeDelegate: AnyObject {
    func didTapThing()
}

//Foo๊ฐ€ ํ’์„ ์„ ๋“ค๊ณ ์žˆ๋Š” ์–ด๋ฆฐ์•„์ด
//ViewController๊ฐ€ ํ’์„ 
class Foo: SomeDelegate {
    init() {
        let vc = ViewController()   // Foo->ViewController ๊ฐ•ํ•œ์ฐธ์กฐ
        vc.delegate = self          // ViewController->Foo ๊ฐ•ํ•œ์ฐธ์กฐ
    }
    
    public func didTapThing() {
        //
    }
}

class ViewController: UIViewController {
    
    weak var delegate: SomeDelegate?
}

Unowned

unowned๋กœ ์„ ์–ธ๋œ ๋ณ€์ˆ˜๋Š” ์ ˆ๋Œ€ nil์ด ๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ unowned ๋ณ€์ˆ˜๋Š” optional๋กœ ์„ ์–ธ๋˜์–ด์„œ๋Š” ์ ˆ๋Œ€ ์•ˆ๋ฉ๋‹ˆ๋‹ค. ๋งˆ์น˜ unwrappingํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. unowned๋Š” ํ•ด์ œ๋œ ๋ฉ”๋ชจ๋ฆฌ ์˜์—ญ์„ ์ ‘๊ทผํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ํ™•์‹คํ•œ ๊ฒฝ์šฐ๋งŒ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค๋Š” ๋œป์ž…๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์•ฑ์ด crashํ•ฉ๋‹ˆ๋‹ค.
ํ•ด๋‹น ๋ณ€์ˆ˜๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๋Š” ๊ฐ์ฒด์˜ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ํ•ด์ œ๋œ ์ดํ›„์—๋Š” ํ•ด๋‹น ์˜์—ญ์„ ๊ฐ€๋ฆฌํ‚ค์ง€ ์•Š๋Š”๋‹ค๋Š” ํ™•์‹ ์ด ์žˆ๋‹ค๋ฉด ๋‹น์‹ ์€ unowned๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  let changeColorToRed = DispatchWorkItem { [unowned self] in
  	guard let self = self else { return }
    self.view.backgroundColor = .red
  }
  • Escaping vs non-escaping closures
    • Non-escaping closures๋Š” scope์•ˆ์—์„œ ์ฆ‰์‹œ ์‹คํ–‰๋œ๋‹ค. ์ €์žฅ๋˜๊ฑฐ๋‚˜ ๋‚˜์ค‘์— ์‹คํ–‰๋  ์ˆ˜ ์—†๋‹ค.
      • strong reference cycle์„ ์œ ๋ฐœํ•˜์ง€ ์•Š์œผ๋‹ˆ weak๋‚˜ unowned๋ฅผ ์‚ฌ์šฉํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค
    • Escaping closures๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ๊ฒฝ์šฐ์—๋งŒ weak๋‚˜ unowned๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
      + property์— ์ €์žฅ๋˜๊ฑฐ๋‚˜ ๋‹ค๋ฅธ ํด๋กœ์ €๋กœ ๋„˜๊ฒจ์งˆ ๋•Œ
      + ํด๋กœ์ € ์•ˆ์— object(such as self)๊ฐ€ strong reference๋ฅผ ์œ ์ง€ํ•  ๋•Œ



Retain Cycle ์‹œ๋‚˜๋ฆฌ์˜ค

1. Delegate

์ž์‹ ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๊ฐ–๊ณ  ์žˆ๋Š” ๋ถ€๋ชจ ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ
๋ถ€๋ชจ ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ pop๋์„ ๋•Œ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฐฉ์ง€

class ParentViewController: UIViewController, ChildViewControllerProtocol: class{
    let childViewController = ChildViewController()
    func prepareChildViewController(){
        childViewController.delegate = self
    }
}

protocol ChildViewControllerProtocol {
    // important functions
}

class ChildViewController: UIViewController {
    //var delegate: ChildViewControllerProtocol? ์—ฌ๊ธฐ!
    weak var delegate: ChildViewControllerProtocol?
}

UITableView์—์„œ์˜ ์˜ˆ์‹œ

weak public var dataSource: UITableViewDataSource?
weak public var delegate: UITableViewDelegate?

2. Closure: class์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ reference type(๊ฐ•ํ•œ ์ฐธ์กฐ)

TestClass ๊ฐ์ฒด์˜ ๋‚ด๋ถ€์—์„œ Closure๋กœ, Closure์—์„œ TestClass๊ฐ์ฒด๋กœ ๊ฐ•ํ•œ ์ฐธ์กฐ
-> weak self๋ฅผ captureํ•ด์คŒ์œผ๋กœ์จ ํ•ด๊ฒฐ

class TestClass{
    var aBlock: (()->())? = nil
    let aConstant = 5
    
    init(){
        print("init")
        aBlock = { [weakself] in	// ์—ฌ๊ธฐ!
        //aBlock = {
            print(self.aConstant)
        }
    }
    deinit(){
        print("deinit")
    }
}

var testClass: TestClass? = TestClass()
testClass = nil

closure ๋ธ”๋ก์„ locallyํ•˜๊ฒŒ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด self๋ฅผ weakํ•˜๊ฒŒ captureํ•  ํ•„์š”๊ฐ€ ์—†์Œ
-> ์˜ˆ์‹œ UIView.animateWithDuration

class TestClass {
    let aConstant = 5
    
    init() {
        print("init")
        let aBlock = {
            print(self.aConstant)
        }
    }
    deinit {
        print("deinit")
    }   
}
var testClass: TestClass? = TestClass()
testClass = nil
class TestClass{
    let aConstant = 5
    
    init(){
        print("init")
    }
    deinit(){
        print("deinit")
    }
    func doSomething(){
        UIView.animate(withDuration: 5){
            let aConstant = self.aConstant
            
            // fancy animation ... .        
        }   
    }
}

var testClass: TestClass? = TestClass()
testClass?.doSomething()
testClass = nil

Detecting retain cycles by using log messages in deinit

๋ชจ๋“  View Controller๋งˆ๋‹ค ์ด๋Ÿฌํ•œ ๋ฉ”์†Œ๋“œ๋ฅผ ์ž‘์„ฑํ•ด์ฃผ๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค

let timer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true, block: { [unowned self] (timer: Timer) in
	if self.progressSlider.isTracking { return }
    
	self.progressSlider.value = Float(self.player.currentTime)
})

ํ”„๋กœ์ ํŠธ MusicPlayer๋ฅผ ๋งŒ๋“ค๋ฉด์„œ unowned self๋ฅผ ์ดํ•ดํ•˜์ง€ ๋ชปํ•ด์„œ ๊ฒ€์ƒ‰ํ–ˆ๋‹ค.
๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ž๋™์œผ๋กœ ๊ด€๋ฆฌํ•ด์ฃผ๋Š” ARC์™€ ์—ฐ๊ด€๋œ ๋ถ€๋ถ„์ด๋ฉฐ, ์•ฝํ•œ ์ฐธ์กฐ๋ฅผ ์˜๋ฏธํ•œ๋‹ค.
weak์™€ unowned๊ฐ€ ๋‹ค๋ฅธ ์ ์€ optional, nil์ด ๋  ์ˆ˜ ์—†๊ณ , ํ•ด์ œ ํ›„ ๋ฐ˜๋“œ์‹œ ํ•ด๋‹น ์˜์—ญ์„ ๊ฐ€๋ฆฌํ‚ค๋Š” ์ผ์ด ์—†์–ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.
์ „์—ญ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” Timer, ํด๋ž˜์Šค์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๊ฐ•ํ•œ ์ฐธ์กฐ๋ฅผ ํ•˜๋Š” ํด๋กœ์ €์˜ ์„ฑ์งˆ ๋•Œ๋ฌธ์—,
ํด๋กœ์ € ๋ธ”๋ก์„ weakํ•˜๊ฒŒ captureํ•œ ๊ฒƒ์ด๋‹ค.

0๊ฐœ์˜ ๋Œ“๊ธ€