서브 스크립트는 클래스, 구조체, 열거형에 타입의 요소로 접근할 수 있게 해주는 문법이다.
서브스크립트는 별로도 Getter, Setter를 설정하지 않아도 값을 가져오거나, 입력하는게 가능하다.
한 타입에 여러개의 서브스크립트를 정의할 수 있으며, 다른 타입을 인덱스로 갖는 여러개의 서브스크립트 또한 중복 정의 할 수 있다.
서브 스크립트는 인스턴스의 이름뒤에 대괄호를 써서, 인스턴스 내부의 특정 값에 접근할 수 있다.
subscript(index: Int) -> Int {
get {
// 적절한 서브 스크립트 결괏값
}
set(newValue) {
// 적절한 설정자 역할
}
}
위의 코드는 서브스크립트의 기본적인 구조이다.
값을 가져올때, 접근할때의 적절한 결괏값을 지정해주고 사용하면 된다.
subscript(index: Int) -> Int {
get {
// 적적한 서브스크립트 결괏값
}
}
subscript(index: Int) -> Int {
// 적적한 서브스크립트 결괏값
}
두 번째 코드의 두 서브스크립트는 사실 같은 역할을 한다.
이유는 첫번째 코드는 읽기전용으로 구현된 코드이고, 아래쪽에 아무것도 적지 않은 코드도 읽기전용의 역할을 하기 때문이다!
서브스크립트는 아까도 말했듯 요소에 접근할 수 있게끔 해주는 문법이다.
따라서 구조체, 열거형, 클래스 등에서 사용할 수 있다.
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) -> Student? {
if index < self.number {
return self.students[index]
}
return nil
}
}
let highSchool: School = School()
highSchool.addStudents(names: "ByeongYun", "JiYun", "SangHee", "Junsik")
let aStudent: Student? = highSchool[1]
print("\(aStudent?.number) \(aStudent?.name)") // Optional(1) Optional("JiYun")
좀 길어진 코드에서 서브스크립트만 쏙 빼와보겠다.
subscript(index: Int) -> Student? {
if index < self.number {
return self.students[index]
}
return nil
}
이 서브스크립트의 역할은 인덱스를 받아와서 그 인덱스가 유효한 인덱스일 경우에 Student 구조체(인스턴스)를 반환하는 역할이다.
이렇게 배열처럼 구조체에 접근할 수 있게 끔 해주는 것이 서브스크립트이다.
아까 설명 때 하나의 타입이 여러개의 서브스크립트를 가질 수 있다고 했었다.
다양한 매개변수를 사용하여 서브스크립트를 구현하면, 다양하게 서브스크립트를 사용할 수 있다.
subscript(index: Int) -> Student? { // 첫 번째 서브스크립트
get {
if index < self.number {
return self.students[index]
}
return nil
}
set {
guard var newStudent: Student = newValue else {
return
}
var number: Int = index
if index > self.number {
number = self.number
self.number += 1
}
newStudent.number = number
self.students[number] = newStudent
}
}
첫 번째 서브스크립트는 아까 요구한 인덱스의 구조체를 가져오는 역할도 하고, 특정 번호의 학생을 할당하는 서브스크립트이다.
subscript(name: String) -> Int? { // 두 번째 서브스크립트
get {
return self.students.filter { $0.name == name }.first?.number
}
set {
guard var number: Int = newValue else {
return
}
if number > self.number {
number = self.number
self.number += 1
}
let newStudent: Student = Student(name: name, number: number)
self.students[number] = newStudent
}
}
두 번째 서브스크립트는 학생의 이름을 전달받아서 해당하는 학생이 있으면 번호를 반환하거나 특정 학생의 이름을 해당 번호에 할당하는 서브스크립트이다.
이렇게 하나의 타입에서 여러개의 서브스크립트를 생성하고 사용할 수 있다.
지금까지 설명한 서브스크립트는 인스턴스에서 사용할 수 있는 서브스크립트이다.
그러나 타입 서브스크립트는 조금 다르게 인스턴스가 아니라 타입 자체에 사용할 수 있는 서브크립트이다.
enum School: Int {
case elementary = 1, middle, high, university
static subscript(level: Int) -> School? {
return Self(rawValue: level)
// return School(rawValue: level) 과 같은 표현이다.
}
}
let school: School? = School[2]
print(school) // School.middle
타입 서브스크립트를 구현하려면 스크립트 앞에 static
키워드를 붙여서 구현할 수 있다.
클래스의 경우에는 class
키워드를 사용하면 된다.
오늘은 서브스크립트의 정의, 서브스크립트의 문법, 구현, 복수 서브스크립트, 타입 서브스크립트에 대해 알아보았다.
서브스크립트는 배열과 같은 느낌으로 요소에 접근할 수 있기 때문에 정말 편리한 기능 중 하나이다. 이 점 꼭 기억하고 나중에 사용해보도록 하자.