[TIL] Swift - 예외처리, ARC, 프로토콜, 확장, 제네릭

신승현·2024년 3월 13일

TIL

목록 보기
39/72
post-thumbnail

1️⃣ 예외처리

✅ 예외처리 란?

  • 에러가 발생했을 때, 에러를 알려주는 등에 대한 조치와 대응을 하는 것을 '예외처리'라고 한다.

✅ Error

  • Error 프로토콜을 채택하여 사용자 정의 에러를 정의하여 사용할 수 있다.
enum CaseError : Error {
    case cantChar; // Character를 사용할 수 없음
    case cantSameNumber; // 같은 수는 사용할 수 없음
    case cantUseZero; // 0은 사용할 수 없음
}

✅ throw와 do-catch 문과 try문

  • throwthrows
    • throws는 리턴 값을 반환하기 전에 오류가 발생하면 에러 객체를 반환한다는 의미이며, 오류가 발생할 메소드 제목 옆에 작성하면 된다.
    • throw는 오류가 발생할 구간에 작성한다.
    • throw로 던진 에러는 do-catch문에서 처리한다.
  • try
    • 에러가 발생할 수 있는 코드 블록을 표시하며, 에러 가능성이 있는 함수나 메소드를 호출할 때 사용한다.
    • 해당 코드 블록에서 발생한 에러를 잡거나 처리할 수 있다.(do - catch문)
  • try?
    • do - catch 구문 없이도 사용이 가능하며, 에러 발생 시 nil값을 반환한다.
    • 에러가 발생하지 않으면 리턴 값의 타입은 옵셔널로 반환된다.
  • try!
    • 에러가 발생을 하면 앱이 강제 종료되기 때문에, 오류가 발생하지 않는 확신아래 사용해야 한다.
    • 반환 타입은 옵셔널이 언래핑된 값이 리턴된다.

2️⃣ ARC

✅ ARC 란?

  • Swift의 ARC(Automatic Reference Counting)는 메모리 관리 기법 중 하나로, 객체나 인스턴스가 참조되는 횟수를 추적하여 메모리에서 해제할 시점을 결정한다. 객체가 생성될 때마다 참조 횟수가 1 증가하고, 해당 객체를 참조하는 다른 객체나 변수가 없어지거나 더 이상 사용되지 않을 때 참조 횟수가 1 감소한다. 참조 횟수가 0이 되면 해당 객체는 메모리에서 해제되는 기법이다.
  • ARC의 작동 방식
    1. 객체 생성: 객체가 생성되면 참조 횟수가 1 증가한다.
    2. 객체 참조: 객체를 다른 변수나 상수에 할당하면 해당 객체의 참조 횟수가 1 증가한다.
    3. 참조 해제: 객체의 참조가 없어지면(참조하는 변수나 상수가 없거나 nil이 할당되면) 참조 횟수가 1 감소한다.
    4. Zeroing Weak References: 약한 참조(Weak Reference)는 객체의 참조 횟수를 증가시키지 않고 추적합니다. 객체가 해제되면 약한 참조는 자동으로 nil로 설정된다.

✅ 강한참조와 약한참조 및 비소유 참조

  • 강한 참조 : 참조할 때 디폴트로 참조되는 것으로, 강한 참조를 잘못 사용하면 메모리 누수 문제가 발생할 수 있다.
  • 약한 참조 : 참조되는 대상을 약하게 참조하여 순환 참조를 방지한다.
    • weak를 사용하며, 옵셔널로 선언되는 참조이다.
    • 약한 참조는 참조하는 객체를 강제로 유지하지 않고, 참조 대상이 메모리에서 해제되면 자동으로 nil로 설정되게 한다.
  • 비소유 참조 : 참조되는 대상이 항상 유효한 경우에만 사용하며, 해당 대상이 해제될 수 있는 상황에는 사용하지 않는다.
    • unowned는 옵셔널이 아닌 비소유 참조를 나타낸다.
    • 비소유 참조는 항상 값이 있다고 가정하며, 참조하는 객체가 해제되면 런타임 에러가 발생할 수 있기 때문에 참조 대상이 해제될 수 있는 경우에만 사용해야 한다.

3️⃣ 프로토콜

✅ 프로토콜 이란?

  • 특정 역할을 하기 위한 메소드, 프로퍼티, 기타 요구사항 등을 정의 해놓은 “규약” 혹은 “약속”이며, 설계된 조건만 정의를 하고 제시를 할 뿐 스스로 기능을 구현하지 않는다.
  • 프로토콜에서는 이름과 타입 그리고 gettable, settable을 명시하며, 프로퍼티는 항상 var로 선언해야 한다.
protocol Coordination
 {
    var top: String { get set }
    var pants: String { get set }

    init(top: String, pants: String)

    func checkCoordination()
}

protocol Hair {
    var hair: String { get }

    func checkHairStyle()
}

struct Person: Coordination, Hair {
    var top: String
    var pants: String
    let hair: String = "포마드"

    func checkHairStyle() {
        print("오늘의 헤어스타일은 \(hair)스타일")
    }

    func checkCoordination() {
        print("상의: \(top)\n하의: \(pants)")
    }

    init(top: String, pants: String) {
        self.top = top
        self.pants = pants
    }     
}

let safari: Person = Person(top: "긴팔", pants: "반바지")
safari.checkHairStyle()
safari.checkCoordination()
//오늘의 헤어스타일은 포마드스타일
//상의: 긴팔
//하의: 반바지

✅ associatedtype, typealias

  • associatedtype : associatedtype은 프로토콜 내에서 실제 타입을 명시하지 않고, 해당 프로토콜을 채택하는 타입에서 실제 타입을 결정하도록 하는데 사용된다.
  • tyypealias : typealias는 기존 타입에 대해 새로운 이름을 지정하거나 복잡한 타입에 대한 간결한 별칭을 생성할 때 사용되며, 긴 이름을 간략하게 대체할 때 유용하다.
protocol Container {
    associatedtype Item // 연관 타입
    var count: Int { get }
    mutating func append(_ item: Item)
    func item(at index: Int) -> Item
}

struct IntContainer: Container {
    typealias Item = Int // 연관 타입을 Int로 typealias하여 구현
    var items = [Item]()
    
    var count: Int {
        return items.count
    }
    
    mutating func append(_ item: Item) {
        items.append(item)
    }
    
    func item(at index: Int) -> Item {
        return items[index]
    }
}

var intBox = IntContainer()
intBox.append(5)
intBox.append(10)
print(intBox.item(at: 0)) // 출력: 5

4️⃣ 확장

✅ 확장(extension) 이란?

  • 확장은 기존 타입에 기능을 추가하는 수평 확장하는 개념이며, structure, class, enum, protocol 타입에 새로운 기능을 추가할 수 있다.
extension 확장할 타입 이름 {
	 //타입에 추가될 새로운 기능 구현
}

extension 확장할 타입 이름: 프로토콜1, 프로토콜2, 프로토콜3 {
	//프로토콜 요구사항 구현
}

✅ 확장이 가능한 것과 불가능한 것

  • 확장이 가능한 것

    1. 새로운 계산된 속성(Computed Property) 추가
    2. 새로운 인스턴스/타입 메서드 추가
    3. 새로운 초기화(Initializer) 추가
    4. 프로토콜 채택(Protocol Conformance)
    5. 서브스크립트 추가(Subscripting)
    6. 중첩 타입(Nested Type) 추가
  • 확장이 불가능한 것

    1. 저장 프로퍼티(Stored Property) 추가
    2. 기존 기능의 재정의(Override)
    3. 초기화 메서드(Initializer)의 재정의
    4. 기존 타입의 저장된 프로퍼티에 기본값 설정

5️⃣ 제네릭

✅ 제네릭 이란?

  • 함수, 타입 및 데이터 구조에 대한 유연하고 추상적인 코드를 작성할 수 있게 해주는 기능으로, 다양한 타입에서 작동하도록 일반화된 코드를 작성할 수 있다.
  • 하나의 타입 매개변수를 갖지 않고 여러 개의 타입 매개변수를 갖고 싶다면, 여러 개의 타입 매개변수를 지정해줄 수도 있다. [ eg: <T, U> ]
// 딕셔너리 예시
@frozen public struct Dictionary<Key, Value> where Key : Hashable {

    /// The element type of a dictionary: a tuple containing an individual
    /// key-value pair.
    public typealias Element = (key: Key, value: Value)


var fruitsInventory: Dictionary<String, Int> = [:]
fruitsInventory["apple"] = 3
profile
개발자

0개의 댓글