궁금한 내용을 검색해보세요!
이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
서근 개발노트
티스토리에 팔로잉
SWIFT/Grammar

Swift : 기초문법 [서브스크립트(subscript)]

서근
QUOTE THE DAY

-
Written by SeogunSEOGUN

반응형

본 게시글은 yagom님과소들이님의 게시글을 참고하여 작성되었습니다.

 

서브스크립트

클래스, 구조체, 열거형에는 컬렉션, 리스트, 시퀀스[각주:1] 등 타입의 요소에 접근하는 단축 문법 서브스크립트(Subscript)를 정의할 수 있다.

 

별도의 설정자(Setter)와 접근자(Getter)등의 메서드를 구현하지 않아도 인덱스를 통해 값을 설정하거나 가져올 수 있다. 

someArray[index] 
//someArray인스턴스의 index를 통해 해당 인덱스 값 접근

someDictionary[key] 
// someDictionary라는 Dictionary의 key를 통해 해당값을 가져옴
  • 클래스, 구조체는 필요한 만큼 서브스크립트 구현 가능
  • 서브스크립스트를 여러 개 구현해도 외부에서 서브스크립트를 사용할 땐, 서브스크립트를 사용할 때 전달한 값 타입을 유추해 적절한 서브스크립트를 적절한 것으로 선택하여 실행함
  • 이것을 중복 정의(Subscript Overloading)라고 함
  • 타입에 따라 여러 매개변수를 갖는 경우도 존재함 (보통은 하나의 매개변수를 가짐)
  • 매개변수의 타입과 반환 타입에는 제한이 없음
  • 여러 개의 매개변수를 가질 수 있고, 매개변수 기본값을 가질 수 있음
  • 입출력 매개변수(in-out parameters)는 가질 수 없음

서브스크립트 문법

  • subscript 키워드에 대괄호(_:)를 붙혀 사용함 subscript(_:)
  • 인스턴스 내부의 특정 값에 접근 가능
  • 연산 프로퍼티(접근자, 설정자)나 인스턴스 메서드 문법과 유사한 형태를 지님
  • 매개변수의 개수, 타입, 반환 타입 등을 지정하며, 읽고 쓰기가 가능하도록 구현하거나 읽기 전용으로만 구현 가능
  • subscript를 정의하는 코드는 각 타입 구현부 또는 타입의 익스텐션 구현부에 위치해야 함
  • get-only는 가능하지만, set-only는 불가 
let nums: [Int] = [1, 2, 3, 4]
 
nums[0] //1
nums[1] //2

nums[0] 이것이 서브스크립트 이다. 대괄호 안에 인덱스를 넣어 요소에 접근 하는것!

let numbers: [String: Int] = ["one": 1, "two": 2, "three": 3]

numbers["one"] 
numbers["two"]
numbers["three"]
subscript(index: Int) -> Int {
     get {
       //적절한 서브스크립트 결괏값을 반환함
     }
     set(newValue) {
       //적절한 설정자 역할을 수행함
     }
}

위 코드의 설정자 set(newValue)의 타입은 서브스크립트 반환 타입과 동일하다. 연산 프로퍼티에서 그랬던 것처럼 매개변수를 따로 명시하지 않으면 전달 인자 newValue를 사용할 수 있다. (set의 매개변수를 생략한다면 반드시 newValue 키워드를 사용해야 함)

 

또, 읽기 전용 프로퍼티를 구현할 땐 get이나 set 키워드를 사용하지 않고 적절한 값만 반환해주는 형태로 구현해도 된다.

subscript(index: Int) -> Int {
     get {
        // 적절한 서브스크립트 값 반환
     }
}

subscript(index: Int) -> Int {
    // 적절한 서브스크립트 결괏값 반환
}

이 두 서브스크립트는 동일한 역할을 한다. get 메서드 없이 단순하게 값만을 반환하도록 구현하면 바로 읽기 전용이 되는것이다. 이렇게 연산 프로퍼티와 아주 유사한것을 확인할 수 있다.

 

String도 인덱스로 접근 하고 싶지만 아래처럼 접근하면 에러가 난다.

let Seogun = "서근 개발블로그"
Seogun[1] //error

하지만 서브스크립트를 사용해서 String을 확장해 다음과 같이 작성한다면 가능하다.

extension String {
    subscript(idx: Int) -> String? {
        guard (0..<count).contains(idx) else {
            return nil
        }
        let target = index(startIndex, offsetBy: idx)
        return String(self[target])
    }
}


let Seogun: String = "서근 개발블로그"
Seogun[0] // 서
Seogun[1] // 근
Seogun[40] // nil

서브스크립트 구현

위에서 말했듯이 서브스크립트는..

  • 자신이 가지는 시퀀스, 컬렉션, 리스트 등의 요소를 반환하고 설정할 때 주로 사용
  • 여러 개의 매개변수를 가질 수 있고, 매개변수 기본값을 가질 수 있음
  • 입출력 매개변수는 가질 수 없음

이라고 했는데 이것을 잘 기억하고 아래 구현 코드를 살펴보도록 하자!

struct Student {
    var name: String
    var number: Int
}

class School {
    var number: Int = 0
    var students: [Student] = [Student]()
    
    func addStudent(name: String) {
        let student: Student = Student(name: name, number: self.number)
        self.students.append(student)
        self.number += 1
    }
    
    func addStudents(names: String...) {
        for name in names {
            self.addStudent(name: name)
        }
    }
    
    subscript(index: Int = 0) -> Student? {
        if index < self.number {
            return self.students[index]
        }
        return nil
    }
}

let highSchool: School = School()
highSchool.addStudents(names: "서근", "철수", "민지", "훈이", "석규")

let aStudent: Student? = highSchool[1]
print("\(aStudent?.number) \(aStudent?.name)") //Optional(1) Optional("철수")
Optional("철수")
print(highSchool[]?.name)

subsctriptSchool 클래스의 읽기 전용 서브스크립트 이고, 학생의 번호를 전달 인자로 전달받아서 자신의 students 프로퍼티 인덱스에 맞는 Student 인스턴스를 반환한다. 

 

위 코드에서 subsctript(index: Int = 0)으로 0을 매개변수 기본값으로 가졌지만, 필요하지 않으면 기본값이 없어도 된다.

타입 서브스크립트

여태까지 사용한 서브 스크립트는 인스턴스에서 사용할 수 있는 서브스크립트 이다.

  • 타입 서브스크립트는 인스턴스가 아닌 타입 자체에서 사용 가능한 서브스크립트임
  • static 키워드를 사용함
  • 클래스의 경우 class 키워드를 사용할 수 있음
enum School: Int {
   case elementary = 1, middle, high, university
   
    static subscript(level: Int) -> School? {
        return Self(rawValue: level)
    }
}

let school: School? = School[2]
print(school) //Optional(School.middle)
struct Alphabet {
    static var alphabets: [String] = ["a", "b", "c", "d"]
 
    static subscript(index: Int) -> String {
        return alphabets[index]
    }
}

print("Alphabet 구조체의 첫 번째 인덱스 : \(Alphabet[0])")
//Alphabet 구조체의 첫 번째 인덱스 : a

print("Alphabet 구조체의 세 번째 인덱스 : \(Alphabet[2])")
//Alphabet 구조체의 세 번째 인덱스 : c

 

 

읽어주셔서 감사합니다 🤟

 

 

 

  1. 시퀀스(Sequence)는 직역하면 연속열이 될 수 있으며, 문자 그대로 개개의 원소들을 순서대로 하나씩 순회할 수 있는 타입을 의미한다. 간단하게 말해 Array나 Dictionary 등이 있다. [본문으로]

잘못된 내용이 있으면 언제든 피드백 부탁드립니다.


서근


위처럼 이미지 와 함께 댓글을 작성할 수 있습니다.