프로그래밍중급/시니어 iOS 개발자

Swift에서 subscript란 무엇이며 이를 어떻게 재정의하거나 확장할 수 있습니까? 고급 사용 예를 들어 주세요.

Hintsage AI 어시스턴트로 면접 통과

답변.

Swift에서 subscript는 배열 및 사전과 같이 인덱스를 사용하여 컬렉션, 컨테이너 또는 사용자 정의 유형의 요소에 접근하는 편리한 구문을 제공합니다. 이를 자신의 클래스, 구조체 및 열거형에서 선언하고 재정의할 수 있습니다.

질문의 배경

Subscript는 Swift에서 객체에 대한 인덱스 접근을 통합하기 위한 방법으로 등장했으며, 처음에는 컬렉션에서 시작하여 모든 사용자 정의 유형으로 확장되었습니다. 주요 목적은 표현력과 가독성을 높이고, 데이터 접근 메서드를 배열 작업을 생각나게 하는 '자연스러운' 구문으로 전환하는 것입니다.

문제

다른 언어(예: Java)에서는 구조체의 요소에 접근하기 위해 get/set 메서드를 사용해야 하므로 코드가 방대해집니다. 더 나은 가독성과 데이터 접근 용이성을 위한 노력이 subscript를 낳았습니다. Swift에서는 안전한 접근, getter 및 setter, 발생할 수 있는 오류 및 optional 반환값에 특별한 주의가 필요합니다.

해결책

Subscript는 subscript 키워드를 사용하여 모든 클래스, 구조체 또는 열거형에서 선언할 수 있습니다. 또한 매개변수 및 반환 타입에 따라 여러 개의 오버로드를 지원합니다.

사용자 정의 subscript 예:

struct Matrix { let rows: Int, columns: Int private 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) precondition(column >= 0 && column < columns) return grid[(row * columns) + column] } set { precondition(row >= 0 && row < rows) precondition(column >= 0 && column < columns) grid[(row * columns) + column] = newValue } } } var matrix = Matrix(rows: 2, columns: 2) matrix[1, 1] = 3.14 print(matrix[1, 1]) // 3.14

주요 특징:

  • 서로 다른 매개변수 유형 및 수로 여러 개의 subscript를 정의할 수 있습니다.
  • Subscript는 오직 getter만 가질 수도 있고, 또는 getter/setter를 모두 가질 수도 있습니다.
  • 서로 다른 인덱스 유형, 심지어 범위와 문자열로 subscript를 사용할 수 있습니다.

속임수 질문.

getter 없이 subscript를 선언할 수 있습니까?

네. 읽기 전용 subscript만 필요하면 get만 구현하면 됩니다.

struct ReadOnlyArray { private let items = [1, 2, 3] subscript(index: Int) -> Int { get { return items[index] } } }

하나의 타입에서 서로 다른 시그니처의 여러 subscript를 선언하는 것이 허용됩니까?

네, subscript는 매개변수의 수와 유형에 따라 오버로드될 수 있습니다.

struct Collection { subscript(index: Int) -> String { ... } subscript(key: String) -> Int? { ... } }

protocol에서 subscript를 선언하고 구조체/클래스에서 구현할 수 있습니까?

네, 프로토콜은 subscript 구현을 요구할 수 있으며, 구체적인 타입은 이를 구현해야 합니다.

protocol MySubscriptable { subscript(index: Int) -> String { get set } } struct SubData: MySubscriptable { private var items = ["a", "b"] subscript(index: Int) -> String { get { return items[index] } set { items[index] = newValue } } }

일반적인 오류 및 안티 패턴

  • 경계 검사 없이 안전하지 않은 데이터 접근 수행 (precondition, bounds check)
  • subscript 매개변수의 데이터 유형 오류
  • 불필요한 subscript의 과도한 사용, 메서드를 사용하는 것이 더 나은 경우

실생활 예시

부정적인 케이스

개발자가 경계 검사가 없는 subscript를 구현했습니다. 결과적으로 존재하지 않는 인덱스에 접근할 때 애플리케이션이 런타임 오류로 종료되었습니다.

장점: 코드가 간결해졌습니다.

단점: 애플리케이션이 예기치 않은 오류로 인해 중단되었습니다.

긍정적인 케이스

precondition 및 optional 반환 가능성을 갖춘 subscript 구현.

장점: 코드가 사용자에게 더 안전해졌고, 오류를 처리할 수 있습니다.

단점: 추가적인 오류 처리 로직을 구축해야 하며, 유효하지 않은 인덱스에 대한 동작을 고려해야 합니다.