์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿš€ ์Šค์œ„ํ”„ํŠธ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ์™€ ARC๋ฅผ ํ†ตํ•œ ํšจ์œจ์  ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ: ๊ฐœ๋ฐœ์ž๊ฐ€ ๊ผญ ์•Œ์•„์•ผ ํ•  ํ•ต์‹ฌ ๊ฐ€์ด๋“œ

๐Ÿš€ ์Šค์œ„ํ”„ํŠธ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ์™€ ARC๋ฅผ ํ†ตํ•œ ํšจ์œจ์  ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ: ๊ฐœ๋ฐœ์ž๊ฐ€ ๊ผญ ์•Œ์•„์•ผ ํ•  ํ•ต์‹ฌ ๊ฐ€์ด๋“œ

๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ์—†๋Š” ๊น”๋”ํ•œ ์ฝ”๋“œ, ARC์™€ ํ•จ๊ป˜๋ผ๋ฉด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค! ๐Ÿ’ช

์•ˆ๋…•, ์นœ๊ตฌ! ๐Ÿ˜Š ์˜ค๋Š˜์€ Swift ๊ฐœ๋ฐœ์ž๋ผ๋ฉด ๋ฐ˜๋“œ์‹œ ์•Œ์•„์•ผ ํ•  ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•ด๋ณผ๊นŒ ํ•ด. ์•ฑ์„ ๋งŒ๋“ค๋‹ค ๋ณด๋ฉด "์™œ ๋‚ด ์•ฑ์ด ๋А๋ ค์ง€์ง€?", "๋ฉ”๋ชจ๋ฆฌ ๊ฒฝ๊ณ ๊ฐ€ ๋œจ๋Š”๋ฐ ์–ด๋””์„œ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธด ๊ฑฐ์•ผ?" ๊ฐ™์€ ๊ณ ๋ฏผ์„ ํ•œ ๋ฒˆ์ฏค ํ•ด๋ดค์„ ๊ฑฐ์•ผ. ๋ฐ”๋กœ ๊ทธ ๋‹ต์ด ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ์— ์žˆ์–ด!

Swift๋Š” ARC(Automatic Reference Counting)๋ผ๋Š” ๋ฉ‹์ง„ ์‹œ์Šคํ…œ์„ ํ†ตํ•ด ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ž๋™์œผ๋กœ ๊ด€๋ฆฌํ•ด์ค˜. ํ•˜์ง€๋งŒ "์ž๋™"์ด๋ผ๊ณ  ํ•ด์„œ ์šฐ๋ฆฌ๊ฐ€ ์•„๋ฌด๊ฒƒ๋„ ์‹ ๊ฒฝ ์“ฐ์ง€ ์•Š์•„๋„ ๋œ๋‹ค๋Š” ๊ฑด ์•„๋‹ˆ์•ผ. ์˜คํžˆ๋ ค ARC์˜ ์ž‘๋™ ์›๋ฆฌ๋ฅผ ์ •ํ™•ํžˆ ์ดํ•ดํ•ด์•ผ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜(Memory Leak)๋‚˜ ๊ฐ•ํ•œ ์ˆœํ™˜ ์ฐธ์กฐ(Strong Reference Cycle) ๊ฐ™์€ ๋ฌธ์ œ๋ฅผ ์˜ˆ๋ฐฉํ•  ์ˆ˜ ์žˆ์ง€! ๐ŸŽฏ

Object A RC: 2 Object B RC: 1 ARC Manager Strong Reference Memory Efficient Memory Leak Risk

๐Ÿ“š ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ, ์™œ ์ค‘์š”ํ• ๊นŒ?

์Šค๋งˆํŠธํฐ์˜ ๋ฉ”๋ชจ๋ฆฌ๋Š” ๋ฌดํ•œํ•˜์ง€ ์•Š์•„. ํŠนํžˆ iOS ๊ธฐ๊ธฐ๋Š” ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ๊ฐ€ ์—„๊ฒฉํ•ด์„œ, ์•ฑ์ด ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ณผ๋„ํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๋ฉด ์‹œ์Šคํ…œ์ด ๊ฐ•์ œ๋กœ ์ข…๋ฃŒ์‹œ์ผœ๋ฒ„๋ ค. ์‚ฌ์šฉ์ž ์ž…์žฅ์—์„œ๋Š” "์•ฑ์ด ๊ฐ‘์ž๊ธฐ ๊บผ์กŒ์–ด!"๋ผ๊ณ  ๋А๋ผ๋Š” ์ตœ์•…์˜ ๊ฒฝํ—˜์ด ๋˜๋Š” ๊ฑฐ์ง€. ๐Ÿ˜ฑ

๐Ÿ’ก ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ๊ฐ€ ์ค‘์š”ํ•œ ์ด์œ 

โ€ข ์•ฑ ์•ˆ์ •์„ฑ: ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ์Œ“์ด๋ฉด ๊ฒฐ๊ตญ ํฌ๋ž˜์‹œ๋กœ ์ด์–ด์ ธ
โ€ข ์„ฑ๋Šฅ ์ตœ์ ํ™”: ๋ถˆํ•„์š”ํ•œ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ์€ ์•ฑ์„ ๋А๋ฆฌ๊ฒŒ ๋งŒ๋“ค์–ด
โ€ข ๋ฐฐํ„ฐ๋ฆฌ ์ˆ˜๋ช…: ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ๊ฐ€ ์ž˜๋ชป๋˜๋ฉด CPU ์‚ฌ์šฉ๋Ÿ‰๋„ ์ฆ๊ฐ€ํ•ด
โ€ข ์‚ฌ์šฉ์ž ๊ฒฝํ—˜: ๋ถ€๋“œ๋Ÿฌ์šด ์•ฑ ๋™์ž‘์€ ์ข‹์€ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ์—์„œ ์‹œ์ž‘๋ผ

์˜ˆ์ „ C๋‚˜ Objective-C ์‹œ์ ˆ์—๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ malloc๊ณผ free๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ• ๋‹นํ•˜๊ณ  ํ•ด์ œํ–ˆ์–ด์•ผ ํ–ˆ์–ด. ์‹ค์ˆ˜๋กœ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ•ด์ œํ•˜์ง€ ์•Š์œผ๋ฉด ๋ˆ„์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ , ์ด๋ฏธ ํ•ด์ œ๋œ ๋ฉ”๋ชจ๋ฆฌ์— ์ ‘๊ทผํ•˜๋ฉด ํฌ๋ž˜์‹œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์ง€. ์ •๋ง ๊ณจ์น˜ ์•„ํ”ˆ ์ผ์ด์—ˆ์–ด! ๐Ÿ˜“

ํ•˜์ง€๋งŒ Swift๋Š” ๋‹ค๋ฅด์ง€! ARC ๋•๋ถ„์— ๋Œ€๋ถ€๋ถ„์˜ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ๊ฐ€ ์ž๋™ํ™”๋˜์–ด ์žˆ์–ด. ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ ์ ˆํ•œ ์œ„์น˜์— ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ์ฝ”๋“œ๋ฅผ ์ž๋™์œผ๋กœ ์‚ฝ์ž…ํ•ด์ฃผ๊ฑฐ๋“ . ๊ทธ๋ž˜๋„ ์šฐ๋ฆฌ๊ฐ€ ์ดํ•ดํ•ด์•ผ ํ•  ํ•ต์‹ฌ ๊ฐœ๋…๋“ค์ด ์žˆ์–ด. ์ง€๊ธˆ๋ถ€ํ„ฐ ํ•˜๋‚˜์”ฉ ํŒŒํ—ค์ณ๋ณด์ž! ๐Ÿ”

๐ŸŽฏ ARC์˜ ๊ธฐ๋ณธ ์›๋ฆฌ: Reference Counting์ด๋ž€?

ARC์˜ ํ•ต์‹ฌ์€ ์ฐธ์กฐ ์นด์šดํŒ…(Reference Counting)์ด์•ผ. ์‰ฝ๊ฒŒ ๋งํ•ด์„œ, ๊ฐ ๊ฐ์ฒด๋งˆ๋‹ค "๋‚˜๋ฅผ ์ฐธ์กฐํ•˜๋Š” ๊ฒŒ ๋ช‡ ๊ฐœ์•ผ?"๋ผ๋Š” ์นด์šดํ„ฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ฑฐ์ง€. ์ด ์นด์šดํ„ฐ๊ฐ€ 0์ด ๋˜๋ฉด "์•„๋ฌด๋„ ๋‚˜๋ฅผ ํ•„์š”๋กœ ํ•˜์ง€ ์•Š๋„ค?"๋ผ๊ณ  ํŒ๋‹จํ•˜๊ณ  ๋ฉ”๋ชจ๋ฆฌ์—์„œ ์ž๋™์œผ๋กœ ํ•ด์ œ๋ผ.

๐Ÿ”ข Reference Count์˜ ๋ณ€ํ™” ๊ณผ์ •

1. ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋˜๋ฉด RC = 1
2. ๋‹ค๋ฅธ ๋ณ€์ˆ˜๊ฐ€ ๊ทธ ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ•˜๋ฉด RC += 1
3. ์ฐธ์กฐ๊ฐ€ ๋Š์–ด์ง€๋ฉด RC -= 1
4. RC๊ฐ€ 0์ด ๋˜๋ฉด ๋ฉ”๋ชจ๋ฆฌ์—์„œ ํ•ด์ œ (deinit ํ˜ธ์ถœ)

์‹ค์ œ ์ฝ”๋“œ๋กœ ๋ณด๋ฉด ํ›จ์”ฌ ์ดํ•ดํ•˜๊ธฐ ์‰ฌ์›Œ. ๊ฐ„๋‹จํ•œ ์˜ˆ์ œ๋ฅผ ๋ณผ๊นŒ? ๐Ÿ˜Š

class Person {
    let name: String
    
    init(name: String) {
        self.name = name
        print("\(name)์ด(๊ฐ€) ์ดˆ๊ธฐํ™”๋˜์—ˆ์Šต๋‹ˆ๋‹ค")
    }
    
    deinit {
        print("\(name)์ด(๊ฐ€) ๋ฉ”๋ชจ๋ฆฌ์—์„œ ํ•ด์ œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค")
    }
}

// RC = 1 (์ฒ ์ˆ˜๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ์ฐธ์กฐ๊ฐ€ 1๊ฐœ)
var reference1: Person? = Person(name: "์ฒ ์ˆ˜")

// RC = 2 (์ฒ ์ˆ˜๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ์ฐธ์กฐ๊ฐ€ 2๊ฐœ)
var reference2: Person? = reference1

// RC = 3 (์ฒ ์ˆ˜๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ์ฐธ์กฐ๊ฐ€ 3๊ฐœ)
var reference3: Person? = reference1

// RC = 2 (์ฐธ์กฐ ํ•˜๋‚˜ ์ œ๊ฑฐ)
reference1 = nil

// RC = 1 (์ฐธ์กฐ ํ•˜๋‚˜ ๋” ์ œ๊ฑฐ)
reference2 = nil

// RC = 0 (๋งˆ์ง€๋ง‰ ์ฐธ์กฐ ์ œ๊ฑฐ, deinit ํ˜ธ์ถœ๋จ!)
reference3 = nil
// ์ถœ๋ ฅ: "์ฒ ์ˆ˜์ด(๊ฐ€) ๋ฉ”๋ชจ๋ฆฌ์—์„œ ํ•ด์ œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค"

์œ„ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด reference3 = nil์„ ์‹คํ–‰ํ•˜๋Š” ์ˆœ๊ฐ„ deinit์ด ํ˜ธ์ถœ๋˜๋Š” ๊ฑธ ๋ณผ ์ˆ˜ ์žˆ์–ด. ์™œ๋ƒํ•˜๋ฉด ๊ทธ๋•Œ ๋น„๋กœ์†Œ Reference Count๊ฐ€ 0์ด ๋˜๊ฑฐ๋“ ! ๐ŸŽ‰

๐Ÿ’ก Pro Tip

deinit ๋ฉ”์„œ๋“œ์— print ๋ฌธ์„ ๋„ฃ์–ด๋‘๋ฉด ๊ฐ์ฒด๊ฐ€ ์–ธ์ œ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ํ•ด์ œ๋˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด. ๋””๋ฒ„๊น…ํ•  ๋•Œ ์ •๋ง ์œ ์šฉํ•˜๋‹ˆ๊นŒ ๊ผญ ํ™œ์šฉํ•ด๋ด!

โš ๏ธ ๊ฐ•ํ•œ ์ˆœํ™˜ ์ฐธ์กฐ: ARC์˜ ๊ฐ€์žฅ ํฐ ์ 

ARC๊ฐ€ ์•„๋ฌด๋ฆฌ ๋˜‘๋˜‘ํ•ด๋„ ํ•ด๊ฒฐํ•˜์ง€ ๋ชปํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ํ•˜๋‚˜ ์žˆ์–ด. ๋ฐ”๋กœ ๊ฐ•ํ•œ ์ˆœํ™˜ ์ฐธ์กฐ(Strong Reference Cycle)์•ผ. ๋‘ ๊ฐ์ฒด๊ฐ€ ์„œ๋กœ๋ฅผ ๊ฐ•ํ•˜๊ฒŒ ์ฐธ์กฐํ•˜๋ฉด Reference Count๊ฐ€ ์ ˆ๋Œ€ 0์ด ๋  ์ˆ˜ ์—†์–ด์„œ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•˜์ง€. ๐Ÿ˜ฐ

์‹ค์ œ ์ƒํ™ฉ์„ ์˜ˆ๋กœ ๋“ค์–ด๋ณผ๊ฒŒ. ์‚ฌ๋žŒ(Person)๊ณผ ์•„ํŒŒํŠธ(Apartment)๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ด๋ณด์ž:

class Person {
    let name: String
    var apartment: Apartment?
    
    init(name: String) {
        self.name = name
        print("\(name) ์ดˆ๊ธฐํ™”")
    }
    
    deinit {
        print("\(name) ํ•ด์ œ")
    }
}

class Apartment {
    let unit: String
    var tenant: Person?
    
    init(unit: String) {
        self.unit = unit
        print("์•„ํŒŒํŠธ \(unit) ์ดˆ๊ธฐํ™”")
    }
    
    deinit {
        print("์•„ํŒŒํŠธ \(unit) ํ•ด์ œ")
    }
}

var john: Person? = Person(name: "John")
var unit4A: Apartment? = Apartment(unit: "4A")

// ๊ฐ•ํ•œ ์ˆœํ™˜ ์ฐธ์กฐ ๋ฐœ์ƒ!
john?.apartment = unit4A  // Person -> Apartment
unit4A?.tenant = john     // Apartment -> Person

// ์ฐธ์กฐ๋ฅผ ๋Š์–ด๋„ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ํ•ด์ œ๋˜์ง€ ์•Š์Œ!
john = nil
unit4A = nil
// deinit์ด ํ˜ธ์ถœ๋˜์ง€ ์•Š์Œ = ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜!

์œ„ ์ฝ”๋“œ์˜ ๋ฌธ์ œ์ ์ด ๋ณด์ด์ง€? John์€ Apartment๋ฅผ ๊ฐ•ํ•˜๊ฒŒ ์ฐธ์กฐํ•˜๊ณ , Apartment๋Š” John์„ ๊ฐ•ํ•˜๊ฒŒ ์ฐธ์กฐํ•ด. ๊ทธ๋ž˜์„œ ๋‘˜ ๋‹ค RC๊ฐ€ 1๋กœ ์œ ์ง€๋˜์–ด ์ ˆ๋Œ€ 0์ด ๋  ์ˆ˜ ์—†์–ด. ์ด๊ฒŒ ๋ฐ”๋กœ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜์˜ ์ „ํ˜•์ ์ธ ์˜ˆ์‹œ์•ผ! ๐Ÿ’ฅ

โš ๏ธ ์ฃผ์˜์‚ฌํ•ญ

๊ฐ•ํ•œ ์ˆœํ™˜ ์ฐธ์กฐ๋Š” ์•ฑ์ด ์‹คํ–‰๋˜๋Š” ๋™์•ˆ ๊ณ„์† ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์žก์•„๋จน์–ด. ์‹œ๊ฐ„์ด ์ง€๋‚ ์ˆ˜๋ก ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์ค„์–ด๋“ค๊ณ , ๊ฒฐ๊ตญ ์•ฑ์ด ๋А๋ ค์ง€๊ฑฐ๋‚˜ ํฌ๋ž˜์‹œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด!

Strong Reference Cycle Problem A B Weak Reference Solution A B โ†’

๐Ÿ”“ Weak Reference: ์ˆœํ™˜ ์ฐธ์กฐ ํ•ด๊ฒฐ์˜ ์—ด์‡ 

๊ฐ•ํ•œ ์ˆœํ™˜ ์ฐธ์กฐ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ์ฒซ ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์€ weak reference๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฑฐ์•ผ. weak ํ‚ค์›Œ๋“œ๋ฅผ ๋ถ™์ด๋ฉด Reference Count๋ฅผ ์ฆ๊ฐ€์‹œํ‚ค์ง€ ์•Š์•„. ์ฆ‰, ์•ฝํ•œ ์ฐธ์กฐ๋Š” ๊ฐ์ฒด๋ฅผ ๊ฐ€๋ฆฌํ‚ค์ง€๋งŒ ์†Œ์œ ํ•˜์ง€๋Š” ์•Š๋Š” ๊ฑฐ์ง€! ๐Ÿ”‘

๐ŸŽฏ Weak Reference์˜ ํŠน์ง•

โ€ข Reference Count๋ฅผ ์ฆ๊ฐ€์‹œํ‚ค์ง€ ์•Š์Œ
โ€ข ํ•ญ์ƒ ์˜ต์…”๋„ ํƒ€์ž…์ด์–ด์•ผ ํ•จ (var๋กœ ์„ ์–ธ)
โ€ข ์ฐธ์กฐํ•˜๋˜ ๊ฐ์ฒด๊ฐ€ ํ•ด์ œ๋˜๋ฉด ์ž๋™์œผ๋กœ nil์ด ๋จ
โ€ข ์ฃผ๋กœ delegate ํŒจํ„ด์—์„œ ์‚ฌ์šฉ๋จ

์•„๊นŒ ๋ฌธ์ œ๊ฐ€ ์žˆ๋˜ ์ฝ”๋“œ๋ฅผ weak์œผ๋กœ ๊ณ ์ณ๋ณผ๊นŒ? ๐Ÿ˜Š

class Person {
    let name: String
    var apartment: Apartment?
    
    init(name: String) {
        self.name = name
        print("\(name) ์ดˆ๊ธฐํ™”")
    }
    
    deinit {
        print("\(name) ํ•ด์ œ")
    }
}

class Apartment {
    let unit: String
    weak var tenant: Person?  // weak ํ‚ค์›Œ๋“œ ์ถ”๊ฐ€!
    
    init(unit: String) {
        self.unit = unit
        print("์•„ํŒŒํŠธ \(unit) ์ดˆ๊ธฐํ™”")
    }
    
    deinit {
        print("์•„ํŒŒํŠธ \(unit) ํ•ด์ œ")
    }
}

var john: Person? = Person(name: "John")
var unit4A: Apartment? = Apartment(unit: "4A")

john?.apartment = unit4A
unit4A?.tenant = john  // weak ์ฐธ์กฐ๋ผ RC ์ฆ๊ฐ€ ์•ˆ ํ•จ!

john = nil  // John์˜ RC๊ฐ€ 0์ด ๋˜์–ด ํ•ด์ œ๋จ
// ์ถœ๋ ฅ: "John ํ•ด์ œ"

unit4A = nil  // Apartment๋„ ์ •์ƒ์ ์œผ๋กœ ํ•ด์ œ๋จ
// ์ถœ๋ ฅ: "์•„ํŒŒํŠธ 4A ํ•ด์ œ"

์งœ์ž”! ๐ŸŽ‰ ์ด์ œ ๋‘ ๊ฐ์ฒด ๋ชจ๋‘ ์ •์ƒ์ ์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ํ•ด์ œ๋˜๋Š” ๊ฑธ ๋ณผ ์ˆ˜ ์žˆ์–ด. tenant๋ฅผ weak์œผ๋กœ ์„ ์–ธํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ˆœํ™˜ ์ฐธ์กฐ๊ฐ€ ๊นจ์ง„ ๊ฑฐ์•ผ.

๐Ÿ’ก ์–ธ์ œ weak์„ ์‚ฌ์šฉํ• ๊นŒ?

โ€ข Delegate ํŒจํ„ด (UITableViewDelegate, UITextFieldDelegate ๋“ฑ)
โ€ข Parent-Child ๊ด€๊ณ„์—์„œ Child๊ฐ€ Parent๋ฅผ ์ฐธ์กฐํ•  ๋•Œ
โ€ข Observer ํŒจํ„ด์—์„œ ๊ด€์ฐฐ์ž๊ฐ€ ๊ด€์ฐฐ ๋Œ€์ƒ์„ ์ฐธ์กฐํ•  ๋•Œ
โ€ข ์ผ๋ฐ˜์ ์œผ๋กœ "์†Œ์œ  ๊ด€๊ณ„๊ฐ€ ์•„๋‹Œ" ์ฐธ์กฐ์— ์‚ฌ์šฉ

๐Ÿ’ช Unowned Reference: ๋˜ ๋‹ค๋ฅธ ํ•ด๊ฒฐ์ฑ…

weak๊ณผ ๋น„์Šทํ•˜์ง€๋งŒ ์กฐ๊ธˆ ๋‹ค๋ฅธ unowned reference๋„ ์žˆ์–ด. unowned๋Š” "์ด ๊ฐ์ฒด๋Š” ์ ˆ๋Œ€ nil์ด ๋˜์ง€ ์•Š์„ ๊ฑฐ์•ผ!"๋ผ๊ณ  ํ™•์‹ ํ•  ๋•Œ ์‚ฌ์šฉํ•ด. weak๊ณผ์˜ ์ฐจ์ด์ ์„ ์ •๋ฆฌํ•ด๋ณผ๊ฒŒ:

ํŠน์ง• weak unowned
ํƒ€์ž… ํ•ญ์ƒ ์˜ต์…”๋„ (var) ์˜ต์…”๋„ ์•„๋‹˜ (let ๊ฐ€๋Šฅ)
nil ๊ฐ€๋Šฅ์„ฑ ์ฐธ์กฐ ๋Œ€์ƒ ํ•ด์ œ ์‹œ ์ž๋™์œผ๋กœ nil nil์ด ๋  ์ˆ˜ ์—†์Œ
RC ์ฆ๊ฐ€ ์ฆ๊ฐ€ํ•˜์ง€ ์•Š์Œ ์ฆ๊ฐ€ํ•˜์ง€ ์•Š์Œ
์‚ฌ์šฉ ์‹œ์  ์ฐธ์กฐ๊ฐ€ nil์ด ๋  ์ˆ˜ ์žˆ์„ ๋•Œ ์ฐธ์กฐ๊ฐ€ ์ ˆ๋Œ€ nil์ด ์•ˆ ๋  ๋•Œ
์œ„ํ—˜์„ฑ ์•ˆ์ „ํ•จ ์ž˜๋ชป ์‚ฌ์šฉ ์‹œ ํฌ๋ž˜์‹œ ๊ฐ€๋Šฅ

unowned๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ „ํ˜•์ ์ธ ์˜ˆ์‹œ๋ฅผ ๋ณด์ž. ์‹ ์šฉ์นด๋“œ์™€ ๊ณ ๊ฐ์˜ ๊ด€๊ณ„์•ผ:

class Customer {
    let name: String
    var card: CreditCard?
    
    init(name: String) {
        self.name = name
        print("\(name) ๊ณ ๊ฐ ์ƒ์„ฑ")
    }
    
    deinit {
        print("\(name) ๊ณ ๊ฐ ํ•ด์ œ")
    }
}

class CreditCard {
    let number: String
    unowned let customer: Customer  // unowned ์‚ฌ์šฉ!
    
    init(number: String, customer: Customer) {
        self.number = number
        self.customer = customer
        print("์นด๋“œ \(number) ์ƒ์„ฑ")
    }
    
    deinit {
        print("์นด๋“œ \(number) ํ•ด์ œ")
    }
}

var jane: Customer? = Customer(name: "Jane")
jane?.card = CreditCard(number: "1234-5678", customer: jane!)

jane = nil
// ์ถœ๋ ฅ:
// "Jane ๊ณ ๊ฐ ํ•ด์ œ"
// "์นด๋“œ 1234-5678 ํ•ด์ œ"

์‹ ์šฉ์นด๋“œ๋Š” ํ•ญ์ƒ ๊ณ ๊ฐ์ด ์žˆ์–ด์•ผ ์กด์žฌํ•  ์ˆ˜ ์žˆ์–ด. ๊ณ ๊ฐ ์—†๋Š” ์‹ ์šฉ์นด๋“œ๋Š” ์˜๋ฏธ๊ฐ€ ์—†์ž–์•„? ๊ทธ๋ž˜์„œ customer๋ฅผ unowned๋กœ ์„ ์–ธํ•œ ๊ฑฐ์•ผ. ๊ณ ๊ฐ์ด ํ•ด์ œ๋˜๋ฉด ์นด๋“œ๋„ ์ž๋™์œผ๋กœ ํ•ด์ œ๋˜์ง€! ๐ŸŽด

โš ๏ธ Unowned ์‚ฌ์šฉ ์‹œ ์ฃผ์˜์‚ฌํ•ญ

unowned๋กœ ์ฐธ์กฐํ•œ ๊ฐ์ฒด๊ฐ€ ๋จผ์ € ํ•ด์ œ๋˜๋ฉด ํฌ๋ž˜์‹œ๊ฐ€ ๋ฐœ์ƒํ•ด! ๊ทธ๋ž˜์„œ "์ด ๊ฐ์ฒด๋Š” ์ ˆ๋Œ€ ๋จผ์ € ํ•ด์ œ๋˜์ง€ ์•Š์„ ๊ฑฐ์•ผ"๋ผ๊ณ  100% ํ™•์‹ ํ•  ๋•Œ๋งŒ ์‚ฌ์šฉํ•ด์•ผ ํ•ด. ํ™•์‹ ์ด ์—†๋‹ค๋ฉด weak์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์•ˆ์ „ํ•ด!

๐Ÿ”„ ํด๋กœ์ €์—์„œ์˜ ๊ฐ•ํ•œ ์ˆœํ™˜ ์ฐธ์กฐ

ํด๋กœ์ €(Closure)๋Š” Swift์—์„œ ์ •๋ง ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ๊ธฐ๋Šฅ์ด์•ผ. ๊ทธ๋Ÿฐ๋ฐ ํด๋กœ์ €๋„ ์ฐธ์กฐ ํƒ€์ž…์ด๋ผ์„œ ๊ฐ•ํ•œ ์ˆœํ™˜ ์ฐธ์กฐ๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ์–ด. ํŠนํžˆ ํด๋กœ์ € ์•ˆ์—์„œ self๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์กฐ์‹ฌํ•ด์•ผ ํ•ด! ๐Ÿ˜ฑ

๋ฌธ์ œ๊ฐ€ ๋˜๋Š” ์ฝ”๋“œ๋ฅผ ๋จผ์ € ๋ณผ๊นŒ?

class HTMLElement {
    let name: String
    let text: String?
    
    // ํด๋กœ์ €๊ฐ€ self๋ฅผ ๊ฐ•ํ•˜๊ฒŒ ์ฐธ์กฐ!
    lazy var asHTML: () -> String = {
        if let text = self.text {
            return "<\(self.name)>\(text)<!--\(self.name)-->"
        } else {
            return "<\(self.name) />"
        }
    }
    
    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
        print("\(name) ์š”์†Œ ์ƒ์„ฑ")
    }
    
    deinit {
        print("\(name) ์š”์†Œ ํ•ด์ œ")
    }
}

var heading: HTMLElement? = HTMLElement(name: "h1", text: "์•ˆ๋…•ํ•˜์„ธ์š”")
print(heading?.asHTML() ?? "")

heading = nil
// deinit์ด ํ˜ธ์ถœ๋˜์ง€ ์•Š์Œ! ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฐœ์ƒ!

์œ„ ์ฝ”๋“œ์˜ ๋ฌธ์ œ๋Š” ๋ญ˜๊นŒ? HTMLElement ์ธ์Šคํ„ด์Šค๋Š” asHTML ํด๋กœ์ €๋ฅผ ๊ฐ•ํ•˜๊ฒŒ ์ฐธ์กฐํ•˜๊ณ , ํด๋กœ์ €๋Š” ๋‚ด๋ถ€์—์„œ self๋ฅผ ๊ฐ•ํ•˜๊ฒŒ ์ฐธ์กฐํ•ด. ๋˜ ์ˆœํ™˜ ์ฐธ์กฐ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฑฐ์ง€! ๐Ÿ”„

์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด ์บก์ฒ˜ ๋ฆฌ์ŠคํŠธ(Capture List)๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ด:

class HTMLElement {
    let name: String
    let text: String?
    
    // ์บก์ฒ˜ ๋ฆฌ์ŠคํŠธ๋กœ weak self ์‚ฌ์šฉ!
    lazy var asHTML: () -> String = { [weak self] in
        guard let self = self else {
            return ""
        }
        
        if let text = self.text {
            return "<\(self.name)>\(text)<!--\(self.name)-->"
        } else {
            return "<\(self.name) />"
        }
    }
    
    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
        print("\(name) ์š”์†Œ ์ƒ์„ฑ")
    }
    
    deinit {
        print("\(name) ์š”์†Œ ํ•ด์ œ")
    }
}

var heading: HTMLElement? = HTMLElement(name: "h1", text: "์•ˆ๋…•ํ•˜์„ธ์š”")
print(heading?.asHTML() ?? "")

heading = nil
// ์ถœ๋ ฅ: "h1 ์š”์†Œ ํ•ด์ œ" - ์ •์ƒ์ ์œผ๋กœ ํ•ด์ œ๋จ!

[weak self]๋ฅผ ํด๋กœ์ € ์‹œ์ž‘ ๋ถ€๋ถ„์— ์ถ”๊ฐ€ํ•˜๋ฉด ํด๋กœ์ €๊ฐ€ self๋ฅผ ์•ฝํ•˜๊ฒŒ ์ฐธ์กฐํ•˜๊ฒŒ ๋ผ. ๊ทธ๋ฆฌ๊ณ  guard let self = self๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ์–ธ๋ž˜ํ•‘ํ•ด์„œ ์‚ฌ์šฉํ•˜๋ฉด ๋ผ! ๐Ÿ‘

๐ŸŽฏ ํด๋กœ์ € ์บก์ฒ˜ ๋ฆฌ์ŠคํŠธ ์‚ฌ์šฉ ๊ทœ์น™

โ€ข [weak self]: self๊ฐ€ nil์ด ๋  ์ˆ˜ ์žˆ์„ ๋•Œ (๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ)
โ€ข [unowned self]: self๊ฐ€ ์ ˆ๋Œ€ nil์ด ์•ˆ ๋  ๋•Œ (ํ™•์‹ ์ด ์žˆ์„ ๋•Œ๋งŒ)
โ€ข [weak delegate]: delegate ๊ฐ™์€ ๋‹ค๋ฅธ ๊ฐ์ฒด๋„ ์บก์ฒ˜ ๊ฐ€๋Šฅ
โ€ข ์—ฌ๋Ÿฌ ๊ฐœ ์บก์ฒ˜: [weak self, weak delegate] ํ˜•ํƒœ๋กœ ์‚ฌ์šฉ

๐Ÿ’ก ์‹ค์ „ ํŒ

๋„คํŠธ์›Œํฌ ์š”์ฒญ์ด๋‚˜ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๊ฐ™์€ ๋น„๋™๊ธฐ ์ž‘์—…์˜ completion handler์—์„œ๋Š” ๊ฑฐ์˜ ํ•ญ์ƒ [weak self]๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ด. ์™œ๋ƒํ•˜๋ฉด ์ž‘์—…์ด ์™„๋ฃŒ๋˜๊ธฐ ์ „์— ํ™”๋ฉด์ด ๋‹ซํž ์ˆ˜ ์žˆ๊ฑฐ๋“ !

๐ŸŽจ ์‹ค์ „ ์˜ˆ์ œ: ViewController์—์„œ์˜ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ

iOS ์•ฑ ๊ฐœ๋ฐœ์—์„œ ๊ฐ€์žฅ ํ”ํ•˜๊ฒŒ ๋งˆ์ฃผ์น˜๋Š” ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ์ƒํ™ฉ์„ ์‚ดํŽด๋ณด์ž. ViewController์—์„œ ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ํ•˜๋Š” ๊ฒฝ์šฐ์•ผ:

import UIKit

class ProfileViewController: UIViewController {
    private var userProfile: UserProfile?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        loadUserProfile()
    }
    
    // โŒ ์ž˜๋ชป๋œ ์˜ˆ: ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๊ฐ€๋Šฅ์„ฑ
    func loadUserProfileWrong() {
        NetworkManager.shared.fetchProfile { profile in
            // self๋ฅผ ๊ฐ•ํ•˜๊ฒŒ ์บก์ฒ˜ํ•˜์—ฌ ์ˆœํ™˜ ์ฐธ์กฐ ๋ฐœ์ƒ ๊ฐ€๋Šฅ
            self.userProfile = profile
            self.updateUI()
        }
    }
    
    // โœ… ์˜ฌ๋ฐ”๋ฅธ ์˜ˆ: weak self ์‚ฌ์šฉ
    func loadUserProfile() {
        NetworkManager.shared.fetchProfile { [weak self] profile in
            guard let self = self else { return }
            
            self.userProfile = profile
            self.updateUI()
        }
    }
    
    private func updateUI() {
        // UI ์—…๋ฐ์ดํŠธ ๋กœ์ง
        print("ํ”„๋กœํ•„ ์—…๋ฐ์ดํŠธ ์™„๋ฃŒ")
    }
    
    deinit {
        print("ProfileViewController ํ•ด์ œ")
    }
}

// ๋„คํŠธ์›Œํฌ ๋งค๋‹ˆ์ € ์˜ˆ์‹œ
class NetworkManager {
    static let shared = NetworkManager()
    
    func fetchProfile(completion: @escaping (UserProfile) -> Void) {
        // ๋น„๋™๊ธฐ ๋„คํŠธ์›Œํฌ ์š”์ฒญ ์‹œ๋ฎฌ๋ ˆ์ด์…˜
        DispatchQueue.global().asyncAfter(deadline: .now() + 2) {
            let profile = UserProfile(name: "ํ™๊ธธ๋™", age: 30)
            DispatchQueue.main.async {
                completion(profile)
            }
        }
    }
}

struct UserProfile {
    let name: String
    let age: Int
}

์œ„ ์ฝ”๋“œ์—์„œ loadUserProfile() ๋ฉ”์„œ๋“œ๋ฅผ ๋ณด๋ฉด [weak self]๋ฅผ ์‚ฌ์šฉํ–ˆ์–ด. ๋งŒ์•ฝ ์‚ฌ์šฉ์ž๊ฐ€ ํ”„๋กœํ•„ ํ™”๋ฉด์„ ์—ด์—ˆ๋‹ค๊ฐ€ ๋„คํŠธ์›Œํฌ ์š”์ฒญ์ด ์™„๋ฃŒ๋˜๊ธฐ ์ „์— ๋’ค๋กœ ๊ฐ€๊ธฐ๋ฅผ ๋ˆ„๋ฅด๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ? weak self ๋•๋ถ„์— ViewController๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ํ•ด์ œ๋ผ! ๐ŸŽ‰

๐Ÿ” ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋””๋ฒ„๊น… ๋ฐฉ๋ฒ•

"๋‚ด ์•ฑ์— ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ์žˆ๋Š”์ง€ ์–ด๋–ป๊ฒŒ ์•Œ ์ˆ˜ ์žˆ์–ด?"๋ผ๊ณ  ๊ถ๊ธˆํ•  ๊ฑฐ์•ผ. Xcode๋Š” ๊ฐ•๋ ฅํ•œ ๋””๋ฒ„๊น… ๋„๊ตฌ๋“ค์„ ์ œ๊ณตํ•ด! ๐Ÿ˜Š

๐Ÿ› ๏ธ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ์ฐพ๋Š” ๋ฐฉ๋ฒ•

1. Instruments - Leaks
โ€ข Xcode ๋ฉ”๋‰ด: Product โ†’ Profile (โŒ˜+I)
โ€ข Leaks ํ…œํ”Œ๋ฆฟ ์„ ํƒ
โ€ข ์•ฑ์„ ์‹คํ–‰ํ•˜๋ฉด์„œ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ์‹ค์‹œ๊ฐ„ ๊ฐ์ง€
โ€ข ๋นจ๊ฐ„์ƒ‰ ๋ง‰๋Œ€๊ฐ€ ๋‚˜ํƒ€๋‚˜๋ฉด ๋ˆ„์ˆ˜ ๋ฐœ์ƒ!

2. Debug Memory Graph
โ€ข ์•ฑ ์‹คํ–‰ ์ค‘ Debug Navigator์—์„œ ๋ฉ”๋ชจ๋ฆฌ ๊ทธ๋ž˜ํ”„ ์•„์ด์ฝ˜ ํด๋ฆญ
โ€ข ๋ณด๋ผ์ƒ‰ ๋А๋‚Œํ‘œ(!)๊ฐ€ ์žˆ์œผ๋ฉด ์ˆœํ™˜ ์ฐธ์กฐ ์˜์‹ฌ
โ€ข ๊ฐ์ฒด ๊ฐ„ ์ฐธ์กฐ ๊ด€๊ณ„๋ฅผ ์‹œ๊ฐ์ ์œผ๋กœ ํ™•์ธ ๊ฐ€๋Šฅ

3. deinit ๋กœ๊น…
โ€ข ๋ชจ๋“  ํด๋ž˜์Šค์— deinit ๋ฉ”์„œ๋“œ ์ถ”๊ฐ€
โ€ข print ๋ฌธ์œผ๋กœ ํ•ด์ œ ์‹œ์  ํ™•์ธ
โ€ข ํ•ด์ œ๋˜์–ด์•ผ ํ•˜๋Š”๋ฐ ์•ˆ ๋˜๋ฉด ๋ฌธ์ œ!

์‹ค์ œ๋กœ Debug Memory Graph๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ž์„ธํžˆ ์•Œ์•„๋ณผ๊นŒ? ์•ฑ์„ ์‹คํ–‰ํ•œ ์ƒํƒœ์—์„œ:

// 1. ๋””๋ฒ„๊ทธ ๋ฐ”์—์„œ ๋ฉ”๋ชจ๋ฆฌ ๊ทธ๋ž˜ํ”„ ์•„์ด์ฝ˜ ํด๋ฆญ
// 2. ์™ผ์ชฝ ํŒจ๋„์—์„œ ์˜์‹ฌ๋˜๋Š” ๊ฐ์ฒด ์„ ํƒ
// 3. ์˜ค๋ฅธ์ชฝ ํŒจ๋„์—์„œ ์ฐธ์กฐ ๊ด€๊ณ„ ํ™•์ธ
// 4. ๋ณด๋ผ์ƒ‰ ๋А๋‚Œํ‘œ๊ฐ€ ์žˆ์œผ๋ฉด ์ˆœํ™˜ ์ฐธ์กฐ!

// ์˜ˆ์‹œ: ์ˆœํ™˜ ์ฐธ์กฐ๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ
class Parent {
    var child: Child?
    deinit { print("Parent deinit") }
}

class Child {
    var parent: Parent?  // ์—ฌ๊ธฐ๊ฐ€ ๋ฌธ์ œ!
    deinit { print("Child deinit") }
}

// ํ•ด๊ฒฐ: weak ์‚ฌ์šฉ
class Child {
    weak var parent: Parent?  // โœ… ํ•ด๊ฒฐ!
    deinit { print("Child deinit") }
}

๐Ÿ’ก ๋ฉ”๋ชจ๋ฆฌ ํ”„๋กœํŒŒ์ผ๋ง ํŒ

โ€ข ์ •๊ธฐ์ ์œผ๋กœ Instruments๋กœ ์•ฑ์„ ํ”„๋กœํŒŒ์ผ๋งํ•˜๋Š” ์Šต๊ด€์„ ๋“ค์—ฌ
โ€ข ํŠนํžˆ ํ™”๋ฉด ์ „ํ™˜์ด ๋งŽ์€ ๋ถ€๋ถ„์„ ์ง‘์ค‘์ ์œผ๋กœ ํ…Œ์ŠคํŠธํ•ด
โ€ข ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์ด ๊ณ„์† ์ฆ๊ฐ€ํ•œ๋‹ค๋ฉด ๋ˆ„์ˆ˜๋ฅผ ์˜์‹ฌํ•ด๋ด
โ€ข ์•ฑ์Šคํ† ์–ด ์ถœ์‹œ ์ „์—๋Š” ๋ฐ˜๋“œ์‹œ ๋ฉ”๋ชจ๋ฆฌ ํ…Œ์ŠคํŠธ๋ฅผ ๊ฑฐ์ณ์•ผ ํ•ด!

๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ Best Practices โœ… ์ข‹์€ ์Šต๊ด€ โ€ข Delegate๋Š” ํ•ญ์ƒ weak โ€ข ํด๋กœ์ €์—์„œ [weak self] โ€ข deinit์œผ๋กœ ํ™•์ธ โ€ข ์ •๊ธฐ์ ์ธ ํ”„๋กœํŒŒ์ผ๋ง โ€ข ๋ช…ํ™•ํ•œ ์†Œ์œ  ๊ด€๊ณ„ ์„ค์ • โŒ ํ”ผํ•ด์•ผ ํ•  ๊ฒƒ โ€ข self๋ฅผ ๋ฌด๋ถ„๋ณ„ํ•˜๊ฒŒ ์บก์ฒ˜ โ€ข unowned ๋‚จ์šฉ โ€ข ์ˆœํ™˜ ์ฐธ์กฐ ๋ฐฉ์น˜ โ€ข ๋ฉ”๋ชจ๋ฆฌ ํ…Œ์ŠคํŠธ ์ƒ๋žต โ€ข ํฐ ๊ฐ์ฒด ์žฅ์‹œ๊ฐ„ ๋ณด์œ  ๐Ÿ’ก ํ•ต์‹ฌ ํฌ์ธํŠธ โ€ข ARC๋Š” ์ž๋™์ด์ง€๋งŒ ์™„๋ฒฝํ•˜์ง€ ์•Š์Œ โ€ข ์ˆœํ™˜ ์ฐธ์กฐ ์ฃผ์˜ โ€ข weak/unowned ํ™œ์šฉ ๐ŸŽฏ ์‹ค์ „ ์ฒดํฌ๋ฆฌ์ŠคํŠธ 1. Delegate ํŒจํ„ด โ†’ protocol์—์„œ class ์ œํ•œ + weak var delegate 2. ํด๋กœ์ € ์‚ฌ์šฉ โ†’ ๋น„๋™๊ธฐ ์ž‘์—…์—์„œ๋Š” [weak self] ํ•„์ˆ˜ 3. Parent-Child ๊ด€๊ณ„ โ†’ Child๊ฐ€ Parent ์ฐธ์กฐ ์‹œ weak 4. ๋ฉ”๋ชจ๋ฆฌ ํ…Œ์ŠคํŠธ โ†’ Instruments๋กœ ์ •๊ธฐ ์ ๊ฒ€ ๐Ÿ’ก ํ™•์‹ ์ด ์—†์œผ๋ฉด weak์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค!

๐Ÿ“ฑ ์‹ค์ „ ํŒจํ„ด: Delegate์—์„œ์˜ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ

iOS ๊ฐœ๋ฐœ์—์„œ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” Delegate ํŒจํ„ด์—์„œ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ๋Š” ์ •๋ง ์ค‘์š”ํ•ด. ์ œ๋Œ€๋กœ ํ•˜์ง€ ์•Š์œผ๋ฉด ์ˆœํ™˜ ์ฐธ์กฐ๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ์‰ฝ๊ฑฐ๋“ ! ๐Ÿ˜ฐ

// โœ… ์˜ฌ๋ฐ”๋ฅธ Delegate ํŒจํ„ด ๊ตฌํ˜„

// 1. Protocol์— class ์ œํ•œ ์ถ”๊ฐ€
protocol DataManagerDelegate: AnyObject {
    func didReceiveData(_ data: String)
    func didFailWithError(_ error: Error)
}

class DataManager {
    // 2. delegate๋ฅผ weak์œผ๋กœ ์„ ์–ธ
    weak var delegate: DataManagerDelegate?
    
    func fetchData() {
        // ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ ์‹œ๋ฎฌ๋ ˆ์ด์…˜
        DispatchQueue.global().asyncAfter(deadline: .now() + 1) { [weak self] in
            DispatchQueue.main.async {
                self?.delegate?.didReceiveData("Hello, World!")
            }
        }
    }
    
    deinit {
        print("DataManager ํ•ด์ œ")
    }
}

class MyViewController: UIViewController, DataManagerDelegate {
    private var dataManager: DataManager?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        dataManager = DataManager()
        dataManager?.delegate = self  // weak์ด๋ผ ์ˆœํ™˜ ์ฐธ์กฐ ์—†์Œ!
        dataManager?.fetchData()
    }
    
    func didReceiveData(_ data: String) {
        print("๋ฐ์ดํ„ฐ ์ˆ˜์‹ : \(data)")
    }
    
    func didFailWithError(_ error: Error) {
        print("์—๋Ÿฌ ๋ฐœ์ƒ: \(error)")
    }
    
    deinit {
        print("MyViewController ํ•ด์ œ")
    }
}

// ์‚ฌ์šฉ ์˜ˆ์‹œ
var viewController: MyViewController? = MyViewController()
viewController = nil
// ์ถœ๋ ฅ:
// "MyViewController ํ•ด์ œ"
// "DataManager ํ•ด์ œ"

์œ„ ์ฝ”๋“œ์—์„œ ์ค‘์š”ํ•œ ํฌ์ธํŠธ๊ฐ€ ์„ธ ๊ฐ€์ง€ ์žˆ์–ด:

๐ŸŽฏ Delegate ํŒจํ„ด์˜ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ํ•ต์‹ฌ

1. Protocol์— AnyObject ์ œํ•œ
protocol DataManagerDelegate: AnyObject
โ†’ ํด๋ž˜์Šค๋งŒ ์ฑ„ํƒ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“ค์–ด weak ์‚ฌ์šฉ ๊ฐ€๋Šฅ

2. Delegate๋ฅผ weak์œผ๋กœ ์„ ์–ธ
weak var delegate: DataManagerDelegate?
โ†’ ์ˆœํ™˜ ์ฐธ์กฐ ๋ฐฉ์ง€

3. ํด๋กœ์ €์—์„œ๋„ weak self
[weak self] ์‚ฌ์šฉ์œผ๋กœ ์ด์ค‘ ์•ˆ์ „์žฅ์น˜

โš ๏ธ ํ”ํ•œ ์‹ค์ˆ˜

Protocol์— AnyObject๋ฅผ ๋ถ™์ด์ง€ ์•Š์œผ๋ฉด weak์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์–ด! ์ปดํŒŒ์ผ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€. ํ•ญ์ƒ delegate ํŒจํ„ด์„ ๊ตฌํ˜„ํ•  ๋•Œ๋Š” : AnyObject๋ฅผ ์žŠ์ง€ ๋ง์ž!

๐ŸŽญ ๊ณ ๊ธ‰ ํŒจํ„ด: Capture List์˜ ๋‹ค์–‘ํ•œ ํ™œ์šฉ

์บก์ฒ˜ ๋ฆฌ์ŠคํŠธ๋Š” weak self๋งŒ ์žˆ๋Š” ๊ฒŒ ์•„๋‹ˆ์•ผ. ์ƒํ™ฉ์— ๋”ฐ๋ผ ๋‹ค์–‘ํ•˜๊ฒŒ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์–ด! ๋ช‡ ๊ฐ€์ง€ ๊ณ ๊ธ‰ ํŒจํ„ด์„ ์•Œ์•„๋ณด์ž. ๐Ÿš€

class AdvancedViewController: UIViewController {
    private var networkManager: NetworkManager?
    private var dataProcessor: DataProcessor?
    
    // ํŒจํ„ด 1: ์—ฌ๋Ÿฌ ๊ฐ์ฒด๋ฅผ weak์œผ๋กœ ์บก์ฒ˜
    func fetchAndProcess() {
        networkManager?.fetchData { [weak self, weak dataProcessor] data in
            guard let self = self,
                  let processor = dataProcessor else { return }
            
            let processed = processor.process(data)
            self.updateUI(with: processed)
        }
    }
    
    // ํŒจํ„ด 2: ๊ฐ’ ํƒ€์ž…์„ ์บก์ฒ˜ (๋ณต์‚ฌ๋ณธ ์ƒ์„ฑ)
    func scheduleNotification() {
        let currentCount = 10
        
        Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) { [weak self] _ in
            // currentCount๋Š” ๊ฐ’ ํƒ€์ž…์ด๋ผ ๋ณต์‚ฌ๋จ
            print("Count: \(currentCount)")
            self?.showAlert()
        }
    }
    
    // ํŒจํ„ด 3: unowned self ์‚ฌ์šฉ (ํ™•์‹ ์ด ์žˆ์„ ๋•Œ)
    func setupAnimation() {
        UIView.animate(withDuration: 1.0) { [unowned self] in
            // ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋ธ”๋ก์€ ๋ทฐ๊ฐ€ ์กด์žฌํ•˜๋Š” ๋™์•ˆ๋งŒ ์‹คํ–‰๋˜๋ฏ€๋กœ unowned ์•ˆ์ „
            self.view.alpha = 0.5
        }
    }
    
    // ํŒจํ„ด 4: ํŠน์ • ํ”„๋กœํผํ‹ฐ๋งŒ ์บก์ฒ˜
    func loadImage() {
        let imageView = self.imageView  // ์ง€์—ญ ๋ณ€์ˆ˜๋กœ ๋ณต์‚ฌ
        
        ImageLoader.load(from: url) { [weak self] image in
            // imageView๋Š” ์ด๋ฏธ ์บก์ฒ˜๋˜์–ด self ์—†์ด ์‚ฌ์šฉ ๊ฐ€๋Šฅ
            imageView.image = image
            self?.hideLoadingIndicator()
        }
    }
    
    private var imageView: UIImageView!
    private var url: URL!
    
    private func updateUI(with data: Any) {}
    private func showAlert() {}
    private func hideLoadingIndicator() {}
}

๊ฐ ํŒจํ„ด์˜ ์‚ฌ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์ •๋ฆฌํ•ด๋ณผ๊ฒŒ:

ํŒจํ„ด ์‚ฌ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค ์žฅ์ 
[weak self] ๋น„๋™๊ธฐ ์ž‘์—…, ๋„คํŠธ์›Œํฌ ์š”์ฒญ ๊ฐ€์žฅ ์•ˆ์ „, nil ์ฒดํฌ ๊ฐ€๋Šฅ
[unowned self] ์งง์€ ์• ๋‹ˆ๋ฉ”์ด์…˜, ๋™๊ธฐ ์ž‘์—… ์˜ต์…”๋„ ์–ธ๋ž˜ํ•‘ ๋ถˆํ•„์š”
์—ฌ๋Ÿฌ ๊ฐ์ฒด ์บก์ฒ˜ ๋ณต์žกํ•œ ์˜์กด์„ฑ ๊ด€๊ณ„ ๋ช…์‹œ์  ์˜์กด์„ฑ ๊ด€๋ฆฌ
๊ฐ’ ํƒ€์ž… ์บก์ฒ˜ ํ˜„์žฌ ์ƒํƒœ ์ €์žฅ ํ•„์š” ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€๋Šฅํ•œ ์Šค๋ƒ…์ƒท

๐Ÿ’ก Pro Tip: guard let self = self vs self?

[weak self]๋ฅผ ์‚ฌ์šฉํ•œ ํ›„ ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์œผ๋กœ self๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด:

1. guard let self = self (๊ถŒ์žฅ)
โ†’ ํ•œ ๋ฒˆ๋งŒ ์ฒดํฌํ•˜๊ณ  ์ดํ›„ self๋ฅผ ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉ
โ†’ ์ฝ”๋“œ ๊ฐ€๋…์„ฑ์ด ์ข‹์Œ

2. self?.method()
โ†’ ๋งค๋ฒˆ ์˜ต์…”๋„ ์ฒด์ด๋‹ ํ•„์š”
โ†’ ๊ฐ„๋‹จํ•œ ๊ฒฝ์šฐ์—๋งŒ ์‚ฌ์šฉ

๐Ÿ—๏ธ ๊ตฌ์กฐ์ฒด vs ํด๋ž˜์Šค: ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ด€์ 

Swift์—์„œ๋Š” ๊ตฌ์กฐ์ฒด(Struct)์™€ ํด๋ž˜์Šค(Class) ์ค‘ ์„ ํƒํ•  ์ˆ˜ ์žˆ์–ด. ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ด€์ ์—์„œ ์ด ๋‘˜์˜ ์ฐจ์ด๋ฅผ ์ดํ•ดํ•˜๋Š” ๊ฒŒ ์ค‘์š”ํ•ด! ๐Ÿค”

๐Ÿ“Š ๊ตฌ์กฐ์ฒด vs ํด๋ž˜์Šค ๋น„๊ต

๊ตฌ์กฐ์ฒด (Value Type)
โ€ข ์Šคํƒ(Stack)์— ์ €์žฅ๋จ
โ€ข ๋ณต์‚ฌ๋  ๋•Œ ๊ฐ’์ด ๋ณต์‚ฌ๋จ (Copy-on-Write)
โ€ข ARC ์ ์šฉ ์•ˆ ๋จ (์ž๋™์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ)
โ€ข ์ˆœํ™˜ ์ฐธ์กฐ ๊ฑฑ์ • ์—†์Œ
โ€ข ์ž‘๊ณ  ๊ฐ„๋‹จํ•œ ๋ฐ์ดํ„ฐ์— ์ ํ•ฉ

ํด๋ž˜์Šค (Reference Type)
โ€ข ํž™(Heap)์— ์ €์žฅ๋จ
โ€ข ๋ณต์‚ฌ๋  ๋•Œ ์ฐธ์กฐ๊ฐ€ ๋ณต์‚ฌ๋จ
โ€ข ARC๋กœ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ
โ€ข ์ˆœํ™˜ ์ฐธ์กฐ ์ฃผ์˜ ํ•„์š”
โ€ข ๋ณต์žกํ•œ ๊ฐ์ฒด๋‚˜ ์ƒ์†์ด ํ•„์š”ํ•  ๋•Œ ์‚ฌ์šฉ

// ๊ตฌ์กฐ์ฒด ์˜ˆ์‹œ: ์ˆœํ™˜ ์ฐธ์กฐ ๊ฑฑ์ • ์—†์Œ
struct Point {
    var x: Double
    var y: Double
}

struct Rectangle {
    var origin: Point
    var size: Point
    
    // ๊ตฌ์กฐ์ฒด๋Š” ๊ฐ’ ํƒ€์ž…์ด๋ผ ๋ณต์‚ฌ๋จ
    mutating func move(by offset: Point) {
        origin.x += offset.x
        origin.y += offset.y
    }
}

var rect1 = Rectangle(origin: Point(x: 0, y: 0), 
                       size: Point(x: 100, y: 100))
var rect2 = rect1  // ๊ฐ’์ด ๋ณต์‚ฌ๋จ!
rect2.move(by: Point(x: 10, y: 10))

print(rect1.origin.x)  // 0 (๋ณ€๊ฒฝ ์•ˆ ๋จ)
print(rect2.origin.x)  // 10 (๋ณ€๊ฒฝ๋จ)

// ํด๋ž˜์Šค ์˜ˆ์‹œ: ์ฐธ์กฐ ํƒ€์ž…
class Person {
    var name: String
    var friend: Person?  // ์ˆœํ™˜ ์ฐธ์กฐ ๊ฐ€๋Šฅ์„ฑ!
    
    init(name: String) {
        self.name = name
    }
    
    deinit {
        print("\(name) ํ•ด์ œ")
    }
}

var person1: Person? = Person(name: "์ฒ ์ˆ˜")
var person2: Person? = Person(name: "์˜ํฌ")

person1?.friend = person2
person2?.friend = person1  // ์ˆœํ™˜ ์ฐธ์กฐ!

// ํ•ด๊ฒฐ: weak ์‚ฌ์šฉ
class BetterPerson {
    var name: String
    weak var friend: BetterPerson?  // โœ… ์ˆœํ™˜ ์ฐธ์กฐ ํ•ด๊ฒฐ!
    
    init(name: String) {
        self.name = name
    }
    
    deinit {
        print("\(name) ํ•ด์ œ")
    }
}

๊ทธ๋ ‡๋‹ค๋ฉด ์–ธ์ œ ๊ตฌ์กฐ์ฒด๋ฅผ ์“ฐ๊ณ  ์–ธ์ œ ํด๋ž˜์Šค๋ฅผ ์จ์•ผ ํ• ๊นŒ? ๐Ÿคทโ€โ™‚๏ธ

๐Ÿ’ก ๊ตฌ์กฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•  ๋•Œ

โ€ข ๊ฐ„๋‹จํ•œ ๋ฐ์ดํ„ฐ ๋ชจ๋ธ (์ขŒํ‘œ, ํฌ๊ธฐ, ๋ฒ”์œ„ ๋“ฑ)
โ€ข ๊ฐ’์˜ ๋™๋“ฑ์„ฑ์ด ์ค‘์š”ํ•  ๋•Œ
โ€ข ๋ณต์‚ฌ๋ณธ์ด ๋…๋ฆฝ์ ์œผ๋กœ ๋™์ž‘ํ•ด์•ผ ํ•  ๋•Œ
โ€ข ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ ์•ˆ์ „ํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์„ ๋•Œ

์˜ˆ์‹œ: Point, Size, Range, URL, Date ๋“ฑ

๐Ÿ’ก ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•  ๋•Œ

โ€ข ์ƒ์†์ด ํ•„์š”ํ•  ๋•Œ
โ€ข ๊ฐ์ฒด์˜ ์ •์ฒด์„ฑ(Identity)์ด ์ค‘์š”ํ•  ๋•Œ
โ€ข ์—ฌ๋Ÿฌ ๊ณณ์—์„œ ๊ฐ™์€ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ณต์œ ํ•ด์•ผ ํ•  ๋•Œ
โ€ข Objective-C์™€ ์ƒํ˜ธ์šด์šฉ์ด ํ•„์š”ํ•  ๋•Œ

์˜ˆ์‹œ: ViewController, Manager, Service ๋“ฑ

โšก ์„ฑ๋Šฅ ์ตœ์ ํ™”: ๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ์ ์ธ ์ฝ”๋“œ ์ž‘์„ฑ

๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ๋ฅผ ์ž˜ํ•œ๋‹ค๋Š” ๊ฑด ๋‹จ์ˆœํžˆ ๋ˆ„์ˆ˜๋ฅผ ๋ฐฉ์ง€ํ•˜๋Š” ๊ฒƒ๋งŒ์ด ์•„๋‹ˆ์•ผ. ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ์‚ฌ์šฉํ•ด์„œ ์•ฑ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•˜๋Š” ๊ฒƒ๋„ ํฌํ•จ๋ผ! ๐Ÿ’ช

// โŒ ๋น„ํšจ์œจ์ : ํฐ ๋ฐฐ์—ด์„ ๊ณ„์† ๋ณต์‚ฌ
func processLargeDataBad() {
    var data = Array(repeating: 0, count: 1_000_000)
    
    for i in 0..<data.count {="" data[i]="i" *="" 2="" ๋งค๋ฒˆ="" ๋ณต์‚ฌ="" ๋ฐœ์ƒ="" ๊ฐ€๋Šฅ="" }="" โœ…="" ํšจ์œจ์ :="" ์ธ๋ฑ์Šค๋กœ="" ์ง์ ‘="" ์ ‘๊ทผ="" func="" processlargedatagood()="" var="" data="Array(repeating:" 0,="" count:="" 1_000_000)="" for="" i="" in="" data.indices="" copy-on-write๋กœ="" ์ตœ์ ํ™”๋จ="" โŒ="" ๋น„ํšจ์œจ์ :="" ๋ถˆํ•„์š”ํ•œ="" ๊ฐ์ฒด="" ์ƒ์„ฑ="" class="" imagecache="" private="" cache:="" [string:="" uiimage]="[:]" getimage(for="" key:="" string)="" -=""> UIImage? {
        // ๋งค๋ฒˆ ์ƒˆ๋กœ์šด UIImage ์ƒ์„ฑ (๋น„ํšจ์œจ์ !)
        if cache[key] == nil {
            cache[key] = UIImage(named: key)
        }
        return cache[key]
    }
}

// โœ… ํšจ์œจ์ : lazy ์ดˆ๊ธฐํ™”์™€ ์บ์‹ฑ
class BetterImageCache {
    private var cache: [String: UIImage] = [:]
    
    func getImage(for key: String) -> UIImage? {
        // ์ด๋ฏธ ์žˆ์œผ๋ฉด ์žฌ์‚ฌ์šฉ
        if let cached = cache[key] {
            return cached
        }
        
        // ์—†์„ ๋•Œ๋งŒ ์ƒ์„ฑํ•˜๊ณ  ์บ์‹ฑ
        if let image = UIImage(named: key) {
            cache[key] = image
            return image
        }
        
        return nil
    }
    
    // ๋ฉ”๋ชจ๋ฆฌ ๊ฒฝ๊ณ  ์‹œ ์บ์‹œ ์ •๋ฆฌ
    func clearCache() {
        cache.removeAll()
    }
}

// โœ… ๋ฉ”๋ชจ๋ฆฌ ๊ฒฝ๊ณ  ์ฒ˜๋ฆฌ
class MyViewController: UIViewController {
    private let imageCache = BetterImageCache()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // ๋ฉ”๋ชจ๋ฆฌ ๊ฒฝ๊ณ  ๊ฐ์ง€
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(handleMemoryWarning),
            name: UIApplication.didReceiveMemoryWarningNotification,
            object: nil
        )
    }
    
    @objc private func handleMemoryWarning() {
        print("๋ฉ”๋ชจ๋ฆฌ ๊ฒฝ๊ณ ! ์บ์‹œ ์ •๋ฆฌ ์ค‘...")
        imageCache.clearCache()
        // ๊ธฐํƒ€ ๋ถˆํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ ์ •๋ฆฌ
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
}</data.count>

๐ŸŽฏ ๋ฉ”๋ชจ๋ฆฌ ์ตœ์ ํ™” ์ฒดํฌ๋ฆฌ์ŠคํŠธ

1. ํฐ ๊ฐ์ฒด ๊ด€๋ฆฌ
โ€ข ์ด๋ฏธ์ง€, ๋น„๋””์˜ค ๊ฐ™์€ ํฐ ๋ฆฌ์†Œ์Šค๋Š” ํ•„์š”ํ•  ๋•Œ๋งŒ ๋กœ๋“œ
โ€ข ์‚ฌ์šฉ ํ›„ ์ฆ‰์‹œ ํ•ด์ œ (nil ํ• ๋‹น)
โ€ข ์บ์‹ฑ ์ „๋žต ์ˆ˜๋ฆฝ (๋ฉ”๋ชจ๋ฆฌ vs ์†๋„ ํŠธ๋ ˆ์ด๋“œ์˜คํ”„)

2. ์ปฌ๋ ‰์…˜ ์ตœ์ ํ™”
โ€ข ๋ฐฐ์—ด ํฌ๊ธฐ๋ฅผ ๋ฏธ๋ฆฌ ์˜ˆ์•ฝ (reserveCapacity)
โ€ข ๋ถˆํ•„์š”ํ•œ ๋ณต์‚ฌ ๋ฐฉ์ง€
โ€ข ์ ์ ˆํ•œ ์ž๋ฃŒ๊ตฌ์กฐ ์„ ํƒ (Array vs Set vs Dictionary)

3. Lazy ์ดˆ๊ธฐํ™”
โ€ข ๋ฌด๊ฑฐ์šด ๊ฐ์ฒด๋Š” lazy var๋กœ ์„ ์–ธ
โ€ข ํ•„์š”ํ•  ๋•Œ๋งŒ ์ƒ์„ฑ๋˜๋„๋ก ์ง€์—ฐ ์ดˆ๊ธฐํ™”
โ€ข ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ ์‹œ์  ๋ถ„์‚ฐ

// Lazy ์ดˆ๊ธฐํ™” ์˜ˆ์‹œ
class DataManager {
    // โŒ ์ฆ‰์‹œ ์ดˆ๊ธฐํ™”: ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„๋„ ๋ฉ”๋ชจ๋ฆฌ ์ฐจ์ง€
    let heavyProcessor = HeavyDataProcessor()
    
    // โœ… Lazy ์ดˆ๊ธฐํ™”: ํ•„์š”ํ•  ๋•Œ๋งŒ ์ƒ์„ฑ
    lazy var betterProcessor: HeavyDataProcessor = {
        print("HeavyDataProcessor ์ƒ์„ฑ๋จ")
        return HeavyDataProcessor()
    }()
    
    // โœ… Lazy ํ”„๋กœํผํ‹ฐ๋Š” ํด๋กœ์ €๋กœ ๋ณต์žกํ•œ ์ดˆ๊ธฐํ™” ๊ฐ€๋Šฅ
    lazy var configuration: Configuration = {
        let config = Configuration()
        config.setup()
        config.loadDefaults()
        return config
    }()
}

class HeavyDataProcessor {
    init() {
        print("๋ฌด๊ฑฐ์šด ๊ฐ์ฒด ์ดˆ๊ธฐํ™” ์ค‘...")
        // ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ์ดˆ๊ธฐํ™” ์ž‘์—…
    }
}

class Configuration {
    func setup() {}
    func loadDefaults() {}
}

// ์‚ฌ์šฉ ์˜ˆ์‹œ
let manager = DataManager()
print("DataManager ์ƒ์„ฑ ์™„๋ฃŒ")
// ์ถœ๋ ฅ: "DataManager ์ƒ์„ฑ ์™„๋ฃŒ"
// betterProcessor๋Š” ์•„์ง ์ƒ์„ฑ ์•ˆ ๋จ!

manager.betterProcessor.process()
// ์ถœ๋ ฅ: "HeavyDataProcessor ์ƒ์„ฑ๋จ"
// ์ด์ œ ์ฒ˜์Œ ์‚ฌ์šฉํ•  ๋•Œ ์ƒ์„ฑ๋จ!

๐ŸŽ“ ์‹ค์ „ ํ”„๋กœ์ ํŠธ: ๋ฉ”๋ชจ๋ฆฌ ์•ˆ์ „ํ•œ ์•ฑ ๋งŒ๋“ค๊ธฐ

์ง€๊ธˆ๊นŒ์ง€ ๋ฐฐ์šด ๋‚ด์šฉ์„ ์ข…ํ•ฉํ•ด์„œ ์‹ค์ „ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž! ๊ฐ„๋‹จํ•œ ์ด๋ฏธ์ง€ ๋ทฐ์–ด ์•ฑ์ธ๋ฐ, ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ๊ฐ€ ์™„๋ฒฝํ•˜๊ฒŒ ๋˜์–ด ์žˆ์–ด. ๐Ÿ˜Ž

import UIKit

// MARK: - ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋” (์‹ฑ๊ธ€ํ†ค ํŒจํ„ด)
class ImageDownloader {
    static let shared = ImageDownloader()
    private init() {}
    
    private var downloadTasks: [URL: URLSessionDataTask] = [:]
    
    func downloadImage(
        from url: URL,
        completion: @escaping (Result<uiimage, error="">) -> Void
    ) {
        // ์ด๋ฏธ ๋‹ค์šด๋กœ๋“œ ์ค‘์ด๋ฉด ์ค‘๋ณต ๋ฐฉ์ง€
        guard downloadTasks[url] == nil else { return }
        
        let task = URLSession.shared.dataTask(with: url) { [weak self] data, response, error in
            defer {
                // ์ž‘์—… ์™„๋ฃŒ ํ›„ ํƒœ์Šคํฌ ์ œ๊ฑฐ
                self?.downloadTasks.removeValue(forKey: url)
            }
            
            if let error = error {
                DispatchQueue.main.async {
                    completion(.failure(error))
                }
                return
            }
            
            guard let data = data,
                  let image = UIImage(data: data) else {
                DispatchQueue.main.async {
                    completion(.failure(NSError(domain: "ImageError", code: -1)))
                }
                return
            }
            
            DispatchQueue.main.async {
                completion(.success(image))
            }
        }
        
        downloadTasks[url] = task
        task.resume()
    }
    
    func cancelDownload(for url: URL) {
        downloadTasks[url]?.cancel()
        downloadTasks.removeValue(forKey: url)
    }
    
    deinit {
        print("ImageDownloader deinit")
    }
}

// MARK: - ์ด๋ฏธ์ง€ ์บ์‹œ ๋งค๋‹ˆ์ €
class ImageCacheManager {
    static let shared = ImageCacheManager()
    private init() {
        setupMemoryWarningObserver()
    }
    
    private var cache = NSCache<nsurl, uiimage="">()
    
    private func setupMemoryWarningObserver() {
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(handleMemoryWarning),
            name: UIApplication.didReceiveMemoryWarningNotification,
            object: nil
        )
    }
    
    @objc private func handleMemoryWarning() {
        print("โš ๏ธ ๋ฉ”๋ชจ๋ฆฌ ๊ฒฝ๊ณ ! ์บ์‹œ ์ •๋ฆฌ ์ค‘...")
        cache.removeAllObjects()
    }
    
    func setImage(_ image: UIImage, for url: URL) {
        cache.setObject(image, forKey: url as NSURL)
    }
    
    func getImage(for url: URL) -> UIImage? {
        return cache.object(forKey: url as NSURL)
    }
    
    func clearCache() {
        cache.removeAllObjects()
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
        print("ImageCacheManager deinit")
    }
}

// MARK: - ์ด๋ฏธ์ง€ ๋ทฐ์–ด ViewController
class ImageViewerViewController: UIViewController {
    private let imageView: UIImageView = {
        let iv = UIImageView()
        iv.contentMode = .scaleAspectFit
        iv.translatesAutoresizingMaskIntoConstraints = false
        return iv
    }()
    
    private let activityIndicator: UIActivityIndicatorView = {
        let indicator = UIActivityIndicatorView(style: .large)
        indicator.translatesAutoresizingMaskIntoConstraints = false
        return indicator
    }()
    
    private let imageURL: URL
    private let downloader = ImageDownloader.shared
    private let cacheManager = ImageCacheManager.shared
    
    init(imageURL: URL) {
        self.imageURL = imageURL
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()
        loadImage()
    }
    
    private func setupUI() {
        view.backgroundColor = .white
        view.addSubview(imageView)
        view.addSubview(activityIndicator)
        
        NSLayoutConstraint.activate([
            imageView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            imageView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            imageView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            imageView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
            
            activityIndicator.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            activityIndicator.centerYAnchor.constraint(equalTo: view.centerYAnchor)
        ])
    }
    
    private func loadImage() {
        // ์บ์‹œ ํ™•์ธ
        if let cachedImage = cacheManager.getImage(for: imageURL) {
            print("โœ… ์บ์‹œ์—์„œ ์ด๋ฏธ์ง€ ๋กœ๋“œ")
            imageView.image = cachedImage
            return
        }
        
        // ๋‹ค์šด๋กœ๋“œ
        activityIndicator.startAnimating()
        
        downloader.downloadImage(from: imageURL) { [weak self] result in
            guard let self = self else { return }
            
            self.activityIndicator.stopAnimating()
            
            switch result {
            case .success(let image):
                print("โœ… ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ ์„ฑ๊ณต")
                self.imageView.image = image
                self.cacheManager.setImage(image, for: self.imageURL)
                
            case .failure(let error):
                print("โŒ ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ ์‹คํŒจ: \(error)")
                self.showErrorAlert()
            }
        }
    }
    
    private func showErrorAlert() {
        let alert = UIAlertController(
            title: "์˜ค๋ฅ˜",
            message: "์ด๋ฏธ์ง€๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.",
            preferredStyle: .alert
        )
        alert.addAction(UIAlertAction(title: "ํ™•์ธ", style: .default))
        present(alert, animated: true)
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        // ํ™”๋ฉด์ด ์‚ฌ๋ผ์งˆ ๋•Œ ๋‹ค์šด๋กœ๋“œ ์ทจ์†Œ
        downloader.cancelDownload(for: imageURL)
    }
    
    deinit {
        print("ImageViewerViewController deinit - ๋ฉ”๋ชจ๋ฆฌ ์ •์ƒ ํ•ด์ œ!")
    }
}</nsurl,></uiimage,>

์œ„ ์ฝ”๋“œ์˜ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ํฌ์ธํŠธ๋ฅผ ์ •๋ฆฌํ•ด๋ณผ๊ฒŒ:

๐ŸŽฏ ์ฝ”๋“œ์˜ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ํฌ์ธํŠธ

1. Weak Self ์‚ฌ์šฉ
โ€ข ๋‹ค์šด๋กœ๋“œ completion handler์—์„œ [weak self] ์‚ฌ์šฉ
โ€ข ํ™”๋ฉด์ด ๋‹ซํ˜€๋„ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ์—†์Œ

2. ์บ์‹œ ๊ด€๋ฆฌ
โ€ข NSCache ์‚ฌ์šฉ์œผ๋กœ ์ž๋™ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ
โ€ข ๋ฉ”๋ชจ๋ฆฌ ๊ฒฝ๊ณ  ์‹œ ์ž๋™์œผ๋กœ ์บ์‹œ ์ •๋ฆฌ

3. ์ž‘์—… ์ทจ์†Œ
โ€ข viewWillDisappear์—์„œ ๋‹ค์šด๋กœ๋“œ ์ทจ์†Œ
โ€ข ๋ถˆํ•„์š”ํ•œ ๋„คํŠธ์›Œํฌ ์ž‘์—… ๋ฐฉ์ง€

4. Deinit ํ™•์ธ
โ€ข ๋ชจ๋“  ํด๋ž˜์Šค์— deinit ๊ตฌํ˜„
โ€ข ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ ์—ฌ๋ถ€ ํ™•์ธ ๊ฐ€๋Šฅ

์ด๋Ÿฐ ์‹์œผ๋กœ ์‹ค์ „ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค ๋•Œ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ๋ฅผ ์—ผ๋‘์— ๋‘๊ณ  ์ฝ”๋”ฉํ•˜๋ฉด, ์•ˆ์ •์ ์ด๊ณ  ์„ฑ๋Šฅ ์ข‹์€ ์•ฑ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด! ๐ŸŽ‰

๐ŸŒ ์žฌ๋Šฅ๋„ท์—์„œ Swift ๊ฐœ๋ฐœ ๋ฐฐ์šฐ๊ธฐ

๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ๊ฐ€ ์ฒ˜์Œ์—๋Š” ์–ด๋ ต๊ฒŒ ๋А๊ปด์งˆ ์ˆ˜ ์žˆ์–ด. ํ•˜์ง€๋งŒ ๊ฑฑ์ •ํ•˜์ง€ ๋งˆ! ์žฌ๋Šฅ๋„ท์—๋Š” Swift ๊ฐœ๋ฐœ์„ ๊ฐ€๋ฅด์ณ์ฃผ๋Š” ์ „๋ฌธ๊ฐ€๋“ค์ด ๋งŽ์•„. ์‹ค์ „ ๊ฒฝํ—˜์ด ํ’๋ถ€ํ•œ ๊ฐœ๋ฐœ์ž๋“ค์—๊ฒŒ 1:1๋กœ ๋ฐฐ์šฐ๋ฉด ํ›จ์”ฌ ๋น ๋ฅด๊ฒŒ ์‹ค๋ ฅ์„ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์ง€! ๐Ÿ˜Š

ํŠนํžˆ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ฐ™์€ ๋ณต์žกํ•œ ์ฃผ์ œ๋Š” ํ˜ผ์ž ๊ณต๋ถ€ํ•˜๋‹ค ๋ง‰ํž ๋•Œ๊ฐ€ ๋งŽ์•„. ๊ทธ๋Ÿด ๋•Œ ์žฌ๋Šฅ๋„ท์—์„œ ๋ฉ˜ํ† ๋ฅผ ์ฐพ์•„๋ณด๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ. ์ฝ”๋“œ ๋ฆฌ๋ทฐ๋ฅผ ๋ฐ›๊ฑฐ๋‚˜, ์‹ค์ „ ํ”„๋กœ์ ํŠธ๋ฅผ ํ•จ๊ป˜ ์ง„ํ–‰ํ•˜๋ฉด์„œ ๋ฐฐ์šฐ๋ฉด ์ด๋ก ๋งŒ ๊ณต๋ถ€ํ•  ๋•Œ๋ณด๋‹ค ํ›จ์”ฌ ํšจ๊ณผ์ ์ด๊ฑฐ๋“ ! ๐Ÿ’ช

๐Ÿ’ก ์žฌ๋Šฅ๋„ท ํ™œ์šฉ ํŒ

โ€ข Swift ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ์ „๋ฌธ๊ฐ€์—๊ฒŒ ์ฝ”๋“œ ๋ฆฌ๋ทฐ ๋ฐ›๊ธฐ
โ€ข ์‹ค์ „ ํ”„๋กœ์ ํŠธ ๋ฉ˜ํ† ๋ง์œผ๋กœ ์‹ค๋ ฅ ํ–ฅ์ƒ
โ€ข ๋ง‰ํžˆ๋Š” ๋ถ€๋ถ„์„ ์งˆ๋ฌธํ•˜๊ณ  ๋น ๋ฅด๊ฒŒ ํ•ด๊ฒฐ
โ€ข ํฌํŠธํด๋ฆฌ์˜ค ํ”„๋กœ์ ํŠธ ํ•จ๊ป˜ ๋งŒ๋“ค๊ธฐ

๐Ÿ“š ์ถ”๊ฐ€ ํ•™์Šต ๋ฆฌ์†Œ์Šค์™€ ํŒ

๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ๋ฅผ ๋งˆ์Šคํ„ฐํ•˜๋ ค๋ฉด ๊ณ„์† ์—ฐ์Šตํ•˜๊ณ  ๊ณต๋ถ€ํ•ด์•ผ ํ•ด. ๋ช‡ ๊ฐ€์ง€ ์œ ์šฉํ•œ ๋ฆฌ์†Œ์Šค์™€ ํŒ์„ ๊ณต์œ ํ• ๊ฒŒ! ๐Ÿ“–

๐ŸŽฏ ํ•™์Šต ๋กœ๋“œ๋งต

์ดˆ๊ธ‰ ๋‹จ๊ณ„
โ€ข ARC ๊ธฐ๋ณธ ๊ฐœ๋… ์ดํ•ด
โ€ข Strong, Weak, Unowned ์ฐจ์ด ํŒŒ์•…
โ€ข ๊ฐ„๋‹จํ•œ ์˜ˆ์ œ๋กœ ์‹ค์Šต
โ€ข deinit์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ ํ™•์ธ

์ค‘๊ธ‰ ๋‹จ๊ณ„
โ€ข ํด๋กœ์ € ์บก์ฒ˜ ๋ฆฌ์ŠคํŠธ ํ™œ์šฉ
โ€ข Delegate ํŒจํ„ด ๊ตฌํ˜„
โ€ข Instruments๋กœ ๋ฉ”๋ชจ๋ฆฌ ํ”„๋กœํŒŒ์ผ๋ง
โ€ข ์‹ค์ „ ํ”„๋กœ์ ํŠธ์— ์ ์šฉ

๊ณ ๊ธ‰ ๋‹จ๊ณ„
โ€ข ๋ณต์žกํ•œ ์ˆœํ™˜ ์ฐธ์กฐ ํ•ด๊ฒฐ
โ€ข ๋ฉ”๋ชจ๋ฆฌ ์ตœ์ ํ™” ๊ธฐ๋ฒ•
โ€ข ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ
โ€ข ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ ํ™˜๊ฒฝ์—์„œ์˜ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ

// ์—ฐ์Šต ๋ฌธ์ œ: ๋‹ค์Œ ์ฝ”๋“œ์˜ ๋ฉ”๋ชจ๋ฆฌ ๋ฌธ์ œ๋ฅผ ์ฐพ์•„๋ณด์„ธ์š”!

class Node {
    var value: Int
    var next: Node?
    var previous: Node?  // ๋ฌธ์ œ ๋ฐœ์ƒ ๊ฐ€๋Šฅ!
    
    init(value: Int) {
        self.value = value
    }
    
    deinit {
        print("Node \(value) ํ•ด์ œ")
    }
}

// ์–‘๋ฐฉํ–ฅ ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ ์ƒ์„ฑ
var node1: Node? = Node(value: 1)
var node2: Node? = Node(value: 2)

node1?.next = node2
node2?.previous = node1  // ์ˆœํ™˜ ์ฐธ์กฐ!

node1 = nil
node2 = nil
// deinit์ด ํ˜ธ์ถœ๋˜์ง€ ์•Š์Œ!

// ํ•ด๊ฒฐ์ฑ…: weak ์‚ฌ์šฉ
class BetterNode {
    var value: Int
    var next: BetterNode?
    weak var previous: BetterNode?  // โœ… weak์œผ๋กœ ํ•ด๊ฒฐ!
    
    init(value: Int) {
        self.value = value
    }
    
    deinit {
        print("BetterNode \(value) ํ•ด์ œ")
    }
}

๐Ÿ’ก ์‹ค์ „ ๋””๋ฒ„๊น… ํŒ

1. ๋ฉ”๋ชจ๋ฆฌ ๊ทธ๋ž˜ํ”„ ํ™œ์šฉ
โ€ข Xcode์˜ Debug Memory Graph๋กœ ์‹œ๊ฐ์  ํ™•์ธ
โ€ข ๋ณด๋ผ์ƒ‰ ๋А๋‚Œํ‘œ ์ฐพ๊ธฐ
โ€ข ์ฐธ์กฐ ์ฒด์ธ ์ถ”์ 

2. Instruments ํ™œ์šฉ
โ€ข Leaks ํ…œํ”Œ๋ฆฟ์œผ๋กœ ๋ˆ„์ˆ˜ ๊ฐ์ง€
โ€ข Allocations๋กœ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰ ๋ชจ๋‹ˆํ„ฐ๋ง
โ€ข ์‹œ๊ฐ„์— ๋”ฐ๋ฅธ ๋ฉ”๋ชจ๋ฆฌ ๋ณ€ํ™” ๊ด€์ฐฐ

3. ๋กœ๊น… ์ „๋žต
โ€ข ๋ชจ๋“  ํด๋ž˜์Šค์— deinit ์ถ”๊ฐ€
โ€ข ์ค‘์š”ํ•œ ๊ฐ์ฒด์˜ ์ƒ์„ฑ/ํ•ด์ œ ๋กœ๊ทธ
โ€ข ๋ฉ”๋ชจ๋ฆฌ ๊ฒฝ๊ณ  ๊ฐ์ง€ ๋ฐ ๋Œ€์‘

๐ŸŽฌ ๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ

์ž, ์—ฌ๊ธฐ๊นŒ์ง€ Swift์˜ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ์™€ ARC์— ๋Œ€ํ•ด ๊นŠ์ด ์žˆ๊ฒŒ ์•Œ์•„๋ดค์–ด! ์ฒ˜์Œ์—๋Š” ๋ณต์žกํ•ด ๋ณด์ด์ง€๋งŒ, ํ•ต์‹ฌ ์›๋ฆฌ๋ฅผ ์ดํ•ดํ•˜๊ณ  ๋‚˜๋ฉด ๊ทธ๋ ‡๊ฒŒ ์–ด๋ ต์ง€ ์•Š์•„. ๐Ÿ˜Š

๐ŸŽฏ ํ•ต์‹ฌ ์š”์•ฝ

1. ARC๋Š” ์ž๋™์ด์ง€๋งŒ ์™„๋ฒฝํ•˜์ง€ ์•Š๋‹ค
โ€ข Reference Counting์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ
โ€ข ์ˆœํ™˜ ์ฐธ์กฐ๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ํ•ด๊ฒฐํ•ด์•ผ ํ•จ

2. Weak๊ณผ Unowned๋ฅผ ์ ์ ˆํžˆ ์‚ฌ์šฉํ•˜์ž
โ€ข Delegate๋Š” ํ•ญ์ƒ weak
โ€ข ํด๋กœ์ €์—์„œ๋Š” [weak self]
โ€ข ํ™•์‹ ์ด ์—†์œผ๋ฉด weak์ด ์•ˆ์ „

3. ๋ฉ”๋ชจ๋ฆฌ ํ”„๋กœํŒŒ์ผ๋ง์„ ์Šต๊ด€ํ™”ํ•˜์ž
โ€ข Instruments๋กœ ์ •๊ธฐ ์ ๊ฒ€
โ€ข deinit์œผ๋กœ ํ•ด์ œ ํ™•์ธ
โ€ข ๋ฉ”๋ชจ๋ฆฌ ๊ฒฝ๊ณ  ๋Œ€์‘ ๋กœ์ง ๊ตฌํ˜„

4. ๊ตฌ์กฐ์ฒด์™€ ํด๋ž˜์Šค๋ฅผ ์ ์ ˆํžˆ ์„ ํƒํ•˜์ž
โ€ข ๊ฐ„๋‹จํ•œ ๋ฐ์ดํ„ฐ๋Š” ๊ตฌ์กฐ์ฒด
โ€ข ๋ณต์žกํ•œ ๊ฐ์ฒด๋Š” ํด๋ž˜์Šค
โ€ข ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ด€์  ๊ณ ๋ ค

๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ๋Š” Swift ๊ฐœ๋ฐœ์ž์˜ ํ•„์ˆ˜ ์—ญ๋Ÿ‰์ด์•ผ. ์ฒ˜์Œ์—๋Š” ์‹ค์ˆ˜๋„ ํ•˜๊ณ  ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋„ ๋งŒ๋“ค๊ฒ ์ง€๋งŒ, ๊ทธ๊ฒŒ ๋‹ค ๊ฒฝํ—˜์ด ๋˜์–ด ์‹ค๋ ฅ์ด ์Œ“์ด๋Š” ๊ฑฐ์•ผ. ์ค‘์š”ํ•œ ๊ฑด ํฌ๊ธฐํ•˜์ง€ ์•Š๊ณ  ๊ณ„์† ์—ฐ์Šตํ•˜๋Š” ๊ฑฐ์ง€! ๐Ÿ’ช

ํ˜น์‹œ ๋ง‰ํžˆ๋Š” ๋ถ€๋ถ„์ด ์žˆ๊ฑฐ๋‚˜ ๋” ๊นŠ์ด ๋ฐฐ์šฐ๊ณ  ์‹ถ๋‹ค๋ฉด, ์žฌ๋Šฅ๋„ท์—์„œ ์ „๋ฌธ๊ฐ€์˜ ๋„์›€์„ ๋ฐ›์•„๋ณด๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ. ์‹ค์ „ ๊ฒฝํ—˜์ด ํ’๋ถ€ํ•œ ๊ฐœ๋ฐœ์ž๋“ค๊ณผ ํ•จ๊ป˜ํ•˜๋ฉด ํ›จ์”ฌ ๋น ๋ฅด๊ฒŒ ์„ฑ์žฅํ•  ์ˆ˜ ์žˆ์–ด! ๐Ÿš€

์ž, ์ด์ œ ๋‹น์‹ ๋„ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๋งˆ์Šคํ„ฐ๊ฐ€ ๋  ์ค€๋น„๊ฐ€ ๋˜์—ˆ์–ด! ์˜ค๋Š˜ ๋ฐฐ์šด ๋‚ด์šฉ์„ ์‹ค์ „ ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•ด๋ณด๊ณ , ์•ˆ์ •์ ์ด๊ณ  ์„ฑ๋Šฅ ์ข‹์€ ์•ฑ์„ ๋งŒ๋“ค์–ด๋ณด์ž. ํ™”์ดํŒ…! ๐ŸŽ‰

๐Ÿš€ Happy Coding with Swift! ๐Ÿš€

๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ๋ฅผ ๋งˆ์Šคํ„ฐํ•˜๊ณ  ์ตœ๊ณ ์˜ iOS ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜์–ด๋ณด์„ธ์š”!

๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0