[iOS 11주차] Swift: subscript, precondition, assert

DoyleHWorks·2024년 12월 30일
0

subscript

subscript는 클래스, 구조체, 열거형 등의 인스턴스에서 컬렉션(Collection)처럼 동작하도록 하기 위해 사용되는 기능이다. 예를 들어, 배열이나 딕셔너리에서 요소에 접근하기 위해 대괄호([])를 사용하는 것처럼, 자신만의 타입에서도 이와 같은 동작을 구현할 수 있다.

쉽게 말해, subscript는 인스턴스 내부 데이터에 간단하게 접근할 수 있도록 하는 특수한 메서드이다.


기본 문법

subscript(index: Int) -> Type {
    get {
        // 인덱스에 따른 값을 반환
    }
    set(newValue) {
        // 인덱스에 해당하는 값 설정
    }
}

사용 예시

1. 배열처럼 동작하는 구조체

struct MyCollection {
    private var items = [1, 2, 3, 4, 5]
    
    subscript(index: Int) -> Int {
        get {
            // index 범위 확인
            precondition(index >= 0 && index < items.count, "Index out of range")
            return items[index]
        }
        set(newValue) {
            precondition(index >= 0 && index < items.count, "Index out of range")
            items[index] = newValue
        }
    }
}

var collection = MyCollection()
print(collection[2]) // 출력: 3
collection[2] = 99
print(collection[2]) // 출력: 99

2. 딕셔너리처럼 키-값 형태로 동작

struct KeyValueStore {
    private var storage: [String: String] = [:]
    
    subscript(key: String) -> String? {
        get {
            return storage[key]
        }
        set(newValue) {
            storage[key] = newValue
        }
    }
}

var store = KeyValueStore()
store["name"] = "Alice"
print(store["name"] ?? "Unknown") // 출력: Alice

특징

  1. 여러 매개변수 가능
    subscript는 하나 이상의 매개변수를 받을 수 있다.

    struct Matrix {
        let rows: Int, columns: Int
        var grid: [Double]
        
        init(rows: Int, columns: Int) {
            self.rows = rows
            self.columns = columns
            grid = Array(repeating: 0.0, count: rows * columns)
        }
        
        subscript(row: Int, column: Int) -> Double {
            get {
                precondition(row >= 0 && row < rows && column >= 0 && column < columns, "Index out of range")
                return grid[(row * columns) + column]
            }
            set {
                precondition(row >= 0 && row < rows && column >= 0 && column < columns, "Index out of range")
                grid[(row * columns) + column] = newValue
            }
        }
    }
    
    var matrix = Matrix(rows: 2, columns: 2)
    matrix[0, 1] = 5.5
    print(matrix[0, 1]) // 출력: 5.5
  2. 읽기 전용 subscript
    get만 구현하면 읽기 전용으로 만들 수 있다.

    struct ReadOnly {
        private var items = [10, 20, 30]
        
        subscript(index: Int) -> Int {
            return items[index]
        }
    }
    
    let readOnly = ReadOnly()
    print(readOnly[1]) // 출력: 20
    // readOnly[1] = 40 // 오류: 수정 불가
  3. subscript에 매개변수 이름 생략
    아래와 같이 구현하면 더 간결하게 사용할 수 있다.

    struct Squared {
        subscript(_ number: Int) -> Int {
            return number * number
        }
    }
    
    let squared = Squared()
    print(squared[4]) // 출력: 16

요약

  • subscript는 대괄호([])를 사용해 데이터에 접근하는 기능을 커스터마이징한다.
  • 읽기 전용 또는 읽기/쓰기 방식으로 구현 가능하다.
  • 배열, 딕셔너리처럼 매끄럽게 데이터를 다룰 수 있어 코드의 가독성과 사용성을 높여준다.

precondition

preconditionSwift에서 런타임 중 특정 조건을 검사하고, 조건이 만족되지 않을 경우 프로그램을 종료하는 함수이다. 보통 디버깅 목적으로 사용되며, 조건이 반드시 충족되어야 하는 상황을 명확히 하는 데 유용하다.


precondition의 역할

  1. 디버깅 도구: 조건이 충족되지 않을 경우 에러 메시지를 출력하고, 프로그램이 실행을 멈춘다.
  2. 안전성 보장: 잘못된 상태에서 코드가 실행되지 않도록 방지한다.
  3. 런타임 확인: 런타임 중 조건이 충족되었는지 확인할 때 사용된다.
    이는 주로 배포 환경에서도 실행되는 런타임 검사이다.

문법

precondition(_ condition: Bool, _ message: @autoclosure () -> String = "")
  • condition: 조건을 나타내는 Boolean 값이다. true이면 정상적으로 실행되고, false이면 프로그램이 종료된다.
  • message: 조건이 충족되지 않을 경우 출력되는 메시지이다(선택 사항).

사용 예제

1. 정상적인 조건 검사

let age = 25
precondition(age >= 0, "Age must be non-negative")
print("Age is valid!") // 출력: Age is valid!

2. 조건이 만족되지 않은 경우

let age = -5
precondition(age >= 0, "Age must be non-negative")
// 프로그램 종료, 출력:
// Fatal error: Age must be non-negative

3. 실수 방지 및 디버깅

func divide(_ dividend: Int, by divisor: Int) -> Int {
    precondition(divisor != 0, "Divisor must not be zero")
    return dividend / divisor
}

print(divide(10, by: 2)) // 출력: 5
print(divide(10, by: 0)) // 프로그램 종료, 오류 메시지 출력

응용 예제

배열 인덱스 접근

func getElement(at index: Int, in array: [Int]) -> Int {
    precondition(index >= 0 && index < array.count, "Index out of range")
    return array[index]
}

let numbers = [10, 20, 30, 40]
print(getElement(at: 2, in: numbers)) // 출력: 30
print(getElement(at: 5, in: numbers)) // 프로그램 종료, 오류 메시지 출력

사용자 입력 검증

func validatePassword(_ password: String) {
    precondition(password.count >= 8, "Password must be at least 8 characters")
    print("Password is valid")
}

validatePassword("secure123") // 출력: Password is valid
validatePassword("short")     // 프로그램 종료, 오류 메시지 출력

요약

  • precondition: 조건이 충족되지 않을 경우 프로그램을 종료해 안전성을 확보한다.
  • 배포 빌드에서도 작동하며, 중요한 런타임 조건을 검사하는 데 사용된다.
  • assert와는 다르게 프로덕션 환경에서도 동작하므로, 중요한 상황에서 사용된다.

assert

Swift에서 assert디버그 빌드(Debug Build) 중 특정 조건이 참인지 확인하고, 조건이 거짓일 경우 프로그램을 종료하며 에러 메시지를 출력하는 디버깅 도구이다.

assert는 개발 단계에서 코드의 논리적 결함을 발견하는 데 유용하며, 배포 빌드(Release Build)에서는 비활성화되므로 성능에 영향을 미치지 않는다.


문법

assert(_ condition: Bool, _ message: @autoclosure () -> String = "")
  • condition: 조건을 나타내는 Bool 값이다. true이면 아무 일도 하지 않고, false이면 프로그램이 종료된다.
  • message: 조건이 거짓일 경우 출력될 에러 메시지(선택 사항)이다.

사용 예제

1. 기본적인 사용

let age = 20
assert(age >= 0, "Age must be a non-negative value")
print("Age is valid!") // 출력: Age is valid!

2. 조건이 거짓일 경우

let age = -5
assert(age >= 0, "Age must be a non-negative value")
// 프로그램 종료, 오류 메시지 출력:
// Assertion failed: Age must be a non-negative value

assert의 동작 원리

  1. 디버그 빌드(Debug Build)
    • 조건이 참(true)이면 아무 일도 하지 않는다.
    • 조건이 거짓(false)이면 프로그램이 종료된다.
  2. 배포 빌드(Release Build)
    • 비활성화되므로 assert 코드는 실행되지 않는다.

assertionFailure

assertionFailure는 조건 없이 항상 프로그램을 종료하는 디버깅 도구이다.

  • 주로 논리적으로 도달하면 안 되는 코드 블록에서 사용된다.

예제

func handleUserInput(_ input: String) {
    switch input {
    case "option1":
        print("Option 1 selected")
    case "option2":
        print("Option 2 selected")
    default:
        assertionFailure("Unexpected input: \(input)")
    }
}

handleUserInput("unknown") // 프로그램 종료, 오류 메시지 출력
// Assertion failed: Unexpected input: unknown

assertprecondition의 차이

특징assertprecondition
활성화 조건디버그 빌드(Debug Build)에서만 활성화배포 빌드(Release Build)에서도 활성화
사용 목적개발 중 논리적 결함 확인반드시 충족되어야 하는 런타임 조건 확인
배포 빌드에서 동작 여부동작하지 않음동작함
사용 예테스트 중 임시 조건 검증중요한 런타임 안전성 보장

사용 상황

  • assert 사용 시기:

    • 개발 단계에서 논리적인 결함을 빠르게 발견하고 수정하고자 할 때
    • 예: 테스트 중 올바르지 않은 매개변수가 들어오지 않음을 확인
  • precondition 사용 시기:

    • 배포 환경에서도 조건을 반드시 보장해야 할 때
    • 예: 중요한 런타임 조건 검사(배열 인덱스, 나눗셈의 0 검사 등)

추가 예제

1. 디버깅 도구로서 assert 사용

func divide(_ dividend: Int, by divisor: Int) -> Int {
    assert(divisor != 0, "Divisor must not be zero")
    return dividend / divisor
}

print(divide(10, by: 2)) // 출력: 5
print(divide(10, by: 0)) // 프로그램 종료, 오류 메시지 출력
// Assertion failed: Divisor must not be zero

2. 게임 상태 검사

let playerLives = -1
assert(playerLives >= 0, "Player lives cannot be negative")
// Assertion failed: Player lives cannot be negative

요약

  • assert: 디버깅 단계에서 조건을 확인하고, 조건이 거짓일 경우 프로그램을 종료한다.
  • 배포 빌드에서 비활성화되므로 성능에 영향을 미치지 않음.
  • 디버깅 중 논리적 오류를 빠르게 찾는 데 유용하다.
profile
Reciprocity lies in knowing enough

0개의 댓글