[πŸ“ iOS TIL] #12 ... Swift 심화 문법 정리 #2

TaeUkΒ·2024λ…„ 3μ›” 13일
0

🍎 iOS TIL

λͺ©λ‘ 보기
11/18
post-thumbnail

Swift 심화 문법 κ°•μ˜ 정리 πŸ’» #2

μ ‘κ·Όμ œν•œμž

: λ‹€λ₯Έ μ†ŒμŠ€ νŒŒμΌμ΄λ‚˜ λͺ¨λ“ˆμ˜ μ½”λ“œμ—μ„œ μ½”λ“œ 일뢀에 λŒ€ν•œ 접근을 μ œν•œ

[μ œμ•½ 적음] open < public < internal < fileprivate < private [μ œμ•½ 많음]

μƒμœ„ μš”μ†Œλ³΄λ‹€ ν•˜μœ„ μš”μ†Œκ°€ 더 높은 μ ‘κ·Ό μˆ˜μ€€μ„ κ°€μ§ˆ 수 μ—†μŠ΅λ‹ˆλ‹€.

private struct Car {
	  public var model: String // 🚨 μ—λŸ¬
}

//private의 μ œμ•½μ΄ public보닀 λ”μš± 크기에 μ—λŸ¬κ°€ λ°œμƒ!
- open 			: λͺ¨λ“  μ†ŒμŠ€ νŒŒμΌμ—μ„œ ν•΄λ‹Ή level μ ‘κ·Ό κ°€λŠ₯
				+ λͺ¨λ“  κ³³μ—μ„œ μ„œλΈŒν΄λž˜μ‹± κ°€λŠ₯
                
- public 		: λͺ¨λ“  μ†ŒμŠ€ νŒŒμΌμ—μ„œ ν•΄λ‹Ή level μ ‘κ·Ό κ°€λŠ₯ 
		  		+ 같은 λͺ¨λ“ˆ λ‚΄μ—μ„œλ§Œ μ„œλΈŒν΄λž˜μ‹± κ°€λŠ₯
                
- internal 		: 같은 λͺ¨λ“ˆ λ‚΄μ—μ„œλ§Œ μ ‘κ·Ό κ°€λŠ₯
				+ μ ‘κ·Ό μ œν•œμžλ₯Ό μž‘μ„±ν•˜μ§€ μ•ŠμœΌλ©΄ `internal`둜 νŒλ‹¨

- fileprivate 	: 같은 μ†ŒμŠ€νŒŒμΌ λ‚΄μ—μ„œλ§Œ μ ‘κ·Ό κ°€λŠ₯

- private 		: 클래슀 λ‚΄λΆ€μ—μ„œλ§Œ μ ‘κ·Ό κ°€λŠ₯

fileprivate -> μ„œλ‘œ λ‹€λ₯Έ ν΄λž˜μŠ€κ°€ 같은 ν•˜λ‚˜μ˜ μ†ŒμŠ€ νŒŒμΌμ— fileprivate둜 μ„ μ–Έλ˜μ–΄ μžˆλ‹€λ©΄, 두 ν΄λž˜μŠ€λŠ” μ„œλ‘œ μ ‘κ·Όν•  수 있음

private -> 같은 파일 μ•ˆμ— μžˆμ–΄λ„ μ„œλ‘œ λ‹€λ₯Έ 클래슀이고,Β private둜 μ„ μ–Έλ˜μ–΄ μžˆλ‹€λ©΄ 두 μš”μ†ŒλŠ” μ„œλ‘œ μ ‘κ·Όν•  수 μ—†μŒ


μ˜ˆμ™Έμ²˜λ¦¬

μ—λŸ¬μ²˜λ¦¬

: ν”„λ‘œκ·Έλž¨μ—μ„œ μ—λŸ¬κ°€ λ°œμƒν•œ 상황에 λŒ€μ‘ν•˜κ³  이에 λŒ€μ‘ν•˜λŠ” κ³Όμ •

Error ν”„λ‘œν† μ½œμ„ μ±„νƒν•˜μ—¬ μ‚¬μš©μž μ •μ˜ μ—λŸ¬λ₯Ό μ •μ˜ν•˜μ—¬ μ—λŸ¬ λŒ€μ‘

enum VendingMachineError: Error {
    case invalidSelection
    case insufficientFunds(coinsNeeded: Int)
    case outOfStock
}

throw, do-catch

  • throwsλŠ” 리턴 값을 λ°˜ν™˜ν•˜κΈ° 전에, 였λ₯˜κ°€ λ°œμƒν•˜λ©΄ μ—λŸ¬ 객체λ₯Ό λ°˜ν™˜
  • throwsλŠ” 였λ₯˜κ°€ λ°œμƒν•  κ°€λŠ₯성이 μžˆλŠ” λ©”μ†Œλ“œ 제λͺ© μ˜†μ— μ‚¬μš©
  • throwλŠ” 였λ₯˜κ°€ λ°œμƒν•  κ΅¬κ°„μ—μ„œ μ‚¬μš©

throw둜 λ˜μ§„ μ—λŸ¬λ₯Ό do-catchλ¬Έμ—μ„œ 처리

enum CustomError: Error {
    case outOfBounds
    case invalidInput(String)
} // Error ν”„λ‘œν† μ½œμ„ ν™œμš©ν•˜μ—¬ μ‚¬μš©μžκ°€ 직접 μ—λŸ¬ μ •μ˜


func processValue(_ value: Int) throws -> Int {
    if value < 0 {
        throw CustomError.invalidInput("Value cannot be negative")
    } else if value > 100 {
        throw CustomError.outOfBounds
    }
    return value * 2
} // `throw`문을 ν™œμš©ν•˜μ—¬ 였λ₯˜κ°€ λ°œμƒν•˜μ˜€μ„ λ•Œμ˜ λ™μž‘ μ„€μ •


do {
    let result = try processValue(-10)
    print("Result is \(result)")
} catch CustomError.outOfBounds {
    print("Value is out of bounds!")
} catch CustomError.invalidInput(let errorMessage) {
    print("Invalid Input: \(errorMessage)")
} catch {
    print("An error occurred: \(error)")
} // `do-catch`문을 ν™œμš©ν•˜μ—¬ μ—λŸ¬ 처리
// 좜λ ₯ : Invalid Input: Value cannot be negative

try , try? , try!

  • try
    • μ—λŸ¬κ°€ λ°œμƒν•  수 μžˆλŠ” μ½”λ“œ 블둝을 ν‘œμ‹œ
    • μ—λŸ¬λ₯Ό 던질 수 μžˆλŠ” ν•¨μˆ˜λ‚˜ λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•  λ•Œ μ‚¬μš©
    • ν•΄λ‹Ή μ½”λ“œ λΈ”λ‘μ—μ„œ λ°œμƒν•œ μ—λŸ¬λ₯Ό μž‘κ±°λ‚˜ μ²˜λ¦¬ν•  수 있음 -> do-catch
  • try?
    • do-catch ꡬ문 없이도 μ‚¬μš©μ΄ κ°€λŠ₯
    • μ—λŸ¬ λ°œμƒμ‹œ nil값을 λ°˜ν™˜
    • μ—λŸ¬κ°€ λ°œμƒν•˜μ§€ μ•ŠμœΌλ©΄ 리턴 κ°’μ˜ νƒ€μž…μ€ μ˜΅μ…”λ„λ‘œ λ°˜ν™˜
  • try!
    • μ—λŸ¬κ°€ λ°œμƒμ„ ν•˜λ©΄ 앱이 κ°•μ œ μ’…λ£Œ
    • λ°˜ν™˜ νƒ€μž…μ€ μ˜΅μ…”λ„μ΄ μ–Έλž˜ν•‘λœ 값이 리턴됨
enum MyError: Error {
    case invalidInput
}


func someThrowingFunction(value: Int) throws -> String {
    guard value >= 0 else {
        throw MyError.invalidInput // valueκ°€ 음수인 경우 μ—λŸ¬λ₯Ό 던짐
    }

    return "The value is \(value)"
}


do {
    let result = try someThrowingFunction(value: -2)
    print(result)
} catch {
    print("Error occurred: \(error)") // 음수 값을 μ²˜λ¦¬ν•˜λŠ” μ—λŸ¬
} // do-catchλ¬Έκ³Ό try ν•¨κ»˜ μ‚¬μš©

// try?λ₯Ό μ‚¬μš©ν•˜μ—¬ μ—λŸ¬ μ²˜λ¦¬ν•˜κΈ°
let result1 = try? someThrowingFunction(value: 5) // μœ νš¨ν•œ κ°’ 호좜
print(result1) // Optional("The value is 5") -> μ˜΅μ…”λ„ νƒ€μž…μœΌλ‘œ κ²°κ³Ό λ°˜ν™˜

let result2 = try? someThrowingFunction(value: -2)
print(result2) // nil


let result4 = try! someThrowingFunction(value: -2)
print(result4)
// μ—λŸ¬κ°€ λ°œμƒν•  수 μžˆλŠ” μƒν™©μ—μ„œ try! μ‚¬μš©μœΌλ‘œ 인해, μ—λŸ¬ λ°œμƒ 및 μ•± κ°•μ œ μ’…λ£Œ

ARC와 λ©”λͺ¨λ¦¬ λˆ„μˆ˜

ARC(Automatic Reference Counting)

: Swiftμ—μ„œμ˜ λ©”λͺ¨λ¦¬ 관리 기법 쀑 ν•˜λ‚˜λ‘œ, κ°μ²΄λ‚˜ μΈμŠ€ν„΄μŠ€κ°€ μ°Έμ‘°λ˜λŠ” 횟수λ₯Ό μΆ”μ ν•˜μ—¬ λ©”λͺ¨λ¦¬μ—μ„œ ν•΄μ œν•  μ‹œμ μ„ κ²°μ •

μ°Έμ‘° νšŸμˆ˜κ°€ 0이 되면 ν•΄λ‹Ή κ°μ²΄λŠ” λ©”λͺ¨λ¦¬μ—μ„œ ν•΄μ œ

class Person {
    let name: String
    init(name: String) {
        self.name = name
        print("\(name) is being initialized")
    }
    deinit {
        print("\(name) is being deinitialized")
    }
}

var reference1: Person?
var reference2: Person?
var reference3: Person?

reference1 = Person(name: "John Appleseed") // RC: 1️⃣
// Prints "John Appleseed is being initialized"

reference2 = reference1 // RC: 2️⃣ 
reference3 = reference1 // RC: 3️⃣

reference1 = nil // RC: 2️⃣ -> μ°Έκ³ ν–ˆλ˜ 객체λ₯Ό μ§€μš°λ©΄ RCκ°€ 1μ”© κ°μ†Œ
reference2 = nil // RC: 1️⃣

reference3 = nil // RC: 0️⃣ -> ν•΄λ‹Ή κ°μ²΄λŠ” λ©”λͺ¨λ¦¬μ—μ„œ ν•΄μ œ
// Prints "John Appleseed is being deinitialized"
// ν”„λ‘œνΌν‹° μ˜΅μ €λ²„μ™€ μœ μ‚¬ν•˜κ²Œ μ°Έκ³  μ—¬λΆ€λ₯Ό 계속 ν™•μΈν•˜μ—¬ λ°˜μ‘

κ°•ν•œ μ°Έμ‘° μˆœν™˜(Strong Reference Cycle)

: 두 개 μ΄μƒμ˜ μΈμŠ€ν„΄μŠ€κ°€ μ„œλ‘œκ°€ μ„œλ‘œλ₯Ό κ°•ν•œ 참쑰일 λ•Œ λ°œμƒ

κ°•ν•œ μ°Έμ‘° μˆœν™˜: λ©”λͺ¨λ¦¬κ°€ ν•΄μ œλ˜μ§€ μ•Šκ³  μœ μ§€λ˜μ–΄ λ©”λͺ¨λ¦¬ λˆ„μˆ˜κ°€ λ°œμƒν•˜λŠ” ν˜„μƒ

  • Swift둜 κ°œλ°œν•  λ•Œμ—λŠ” λ©”λͺ¨λ¦¬ λˆ„μˆ˜(Memory Leak)을 주의
  • μ°Έμ‘°λŠ” λ””ν΄νŠΈλ‘œ κ°•ν•œ μ°Έμ‘°(Strong Reference)λ₯Ό μ‚¬μš©

κ°•ν•œ μ°Έμ‘°λ₯Ό 잘λͺ» μ‚¬μš©ν•˜λ©΄ λ©”λͺ¨λ¦¬ λˆ„μˆ˜(Memory Leak) λ¬Έμ œκ°€ λ°œμƒν•  수 있음

κ°•ν•œ μ°Έμ‘° μˆœν™˜ 문제 ν•΄κ²° 방법

  1. μ•½ν•œ μ°Έμ‘°(Weak Reference) μ‚¬μš©
    : μ°Έμ‘°λ˜λŠ” λŒ€μƒμ„ μ•½ν•˜κ²Œ μ°Έμ‘°ν•˜μ—¬ μˆœν™˜ μ°Έμ‘°λ₯Ό 방지
  • weakλŠ” μ˜΅μ…”λ„λ‘œ μ„ μ–Έλ˜λŠ” μ°Έμ‘°

  • μ°Έμ‘° λŒ€μƒμ΄ λ©”λͺ¨λ¦¬μ—μ„œ ν•΄μ œλ˜λ©΄ μžλ™μœΌλ‘œ nil둜 μ„€μ •

두 객체가 μ„œλ‘œλ₯Ό κ°•ν•˜κ²Œ μ°Έμ‘°ν•˜λŠ” 경우, ν•œμͺ½μ„ weak둜 μ„ μ–Έν•˜μ—¬ μˆœν™˜ μ°Έμ‘° 문제λ₯Ό ν•΄κ²°

class Man {
    var name: String
    weak var girlfriend: Woman? 
    //weakλŠ” 클래슀 λ‚΄λΆ€κ°€ μ•„λ‹ˆλΌ μ‚¬μš©ν•˜λŠ” 객체 μ•žμ— 뢙여도 됨

    init(name: String) {
        self.name = name
    }
    deinit { print("Man Deinit!") }
}

class Woman {
    var name: String
    var boyfriend: Man?

    init(name: String) {
        self.name = name
    }
    deinit { print("Woman Deinit!") }
}


var chelosu: Man? = .init(name: "철수")
var yeonghee: Woman? = .init(name: "영희")

chelosu?.girlfriend = yeonghee
yeonghee?.boyfriend = chelosu


chelosu = nil
yeonghee = nil
chelosu?.girlfriend // nil -> `weak`λ₯Ό ν™œμš©ν•œ μ—λŸ¬ 처리
  1. λΉ„μ†Œμœ  μ°Έμ‘°(Unowned Reference)
    : μ°Έμ‘°λ˜λŠ” λŒ€μƒμ΄ 항상 μœ νš¨ν•œ κ²½μš°μ—λ§Œ μ‚¬μš©ν•˜λ©°, ν•΄λ‹Ή λŒ€μƒμ΄ ν•΄μ œλ  수 μžˆλŠ” μƒν™©μ—λŠ” μ‚¬μš©ν•˜μ§€ μ•ŠμŒ
  • unownedλŠ” μ˜΅μ…”λ„μ΄ μ•„λ‹Œ λΉ„μ†Œμœ  μ°Έμ‘°

  • λΉ„μ†Œμœ  μ°Έμ‘°λŠ” 항상 값이 μžˆλ‹€κ³  κ°€μ •ν•˜λ©°, μ°Έμ‘°ν•˜λŠ” 객체가 ν•΄μ œλ˜λ©΄ λŸ°νƒ€μž„ μ—λŸ¬κ°€ λ°œμƒν•  수 있음

unowned μ°Έμ‘°λŠ” μ°Έμ‘° λŒ€μƒμ΄ ν•΄μ œλ  수 μžˆλŠ” κ²½μš°μ—λ§Œ μ‚¬μš©

class Man {
    var name: String
    unowned var girlfriend: Woman?

    init(name: String) {
        self.name = name
    }
    deinit { print("Man Deinit!") }
}

class Woman {
    var name: String
    var boyfriend: Man?

    init(name: String) {
        self.name = name
    }
    deinit { print("Woman Deinit!") }
}

var chelosu: Man? = .init(name: "철수")
var yeonghee: Woman? = .init(name: "영희")

chelosu?.girlfriend = yeonghee
yeonghee?.boyfriend = chelosu


yeonghee = nil
chelosu?.girlfriend // μ—λŸ¬ λ°œμƒ

0개의 λŒ“κΈ€