/Swift
17. 서브스크립트
betterhee
2021. 1. 30. 17:19
클래스, 구조체, 열거형에는 컬렉션, 리스트, 시퀀스 등 타입의 요소에 접근하는 단축 문법인 서브스크립트(Subscript)를 정의할 수 있다.
// 함수 사용
someArray.element(at: index)
someDictionary.value(forKey: key)
// 서브스크립트 사용
someArray[index]
someDictionary[key]
17.1 서브스크립트 문법
서브스크립트 정의 문법
subscript(index: Int) -> Int {
get {
// 적절한 서브스크립트 결괏값 반환
}
set(newValue) { // 매개변수를 따로 명시해주지 않으면 설정자의 암시적 전달인지 newValue 사용 가능
// 적절한 설정자 역할 수행
}
}
읽기 전용 서브스크립트 정의 문법
subscript(index: Int) -> Int {
get {
// 적절한 서브스크립트 결괏값 반환
}
}
subscript(index: Int) -> Int {
// 적절한 서브스크립트 결괏값 반환
}
17.2 서브스크립트 구현
서브스크립트는 자신이 가지는 컬렉션, 리스트, 시퀀스 등의 요소를 반환하고 설정할 때 주로 사용합니다.
함수와 마찬가지로 서브스크립트는 여러 개의 매개변수를 가질 수 있고, 매개변수 기본값을 가질 수 있습니다.
그렇지만 입출력 매개변수(in-out parameters)는 가질 수 없습니다. 1
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: "MiJeong","JuHyun", "JiYoung", "SeongUk", "MoonDuk")
print(highSchool[1]?.name) // Optional("JuHyun")
print(highSchool[]?.name) // Optional("MiJeong")
17.3 복수 서브스크립트
클래스와 구조체는 필요한 만큼 얼마든지 서브스크립트를 구현할 수 있습니다.
서브스크립트를 여러 개 구현해도 외부에서 서브스크립트를 사용할 때 전달한 값의 타입을 유추하여 적절한 서브스크립트를 선택하여 실행합니다.
이렇게 여러 서브스크립트를 한 타입에 구현하는 것을 서브스크립트 중복 정의(Subscript Overloading)라고 합니다.
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)
}
}
// 1.
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
}
}
// 2.
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
}
}
// 3.
subscript(name: String, number: Int) -> Student? {
return self.students.filter{ $0.name == name && $0.number == number }.first
}
}
let highSchool: School = School()
highSchool.addStudents(names: "MiJeong","JuHyun", "JiYoung", "SeongUk", "MoonDuk")
// 1.
// subscript(index: Int) -> Student?
print(highSchool[1]) // Optional(Student(name: "JuHyun", number: 1))
// 2.
// subscript(name: String) -> Int?
print(highSchool["MiJeong"]) // Optional(0)
print(highSchool["DongJin"]) // nil
highSchool[0] = Student(name: "HongEui", number: 0)
highSchool["MangGu"] = 1
print(highSchool["JuHyun"]) // nil
print(highSchool["MangGu"]) // Optional(1)
// 3.
// subscript(name: String, number: Int) -> Student?
print(highSchool["SeongUk", 3]) // Optional(Student(name: "SeongUk", number: 3))
print(highSchool["HeeJin", 3]) // nil
이처럼 서브스크립트는 메서드인듯 아닌듯, 연산 프로퍼티인 듯 아닌 듯 중간 형태를 띄며 인스턴스 이름 뒤에 대괄호만 써서 편리하게 내부 값에 접근하고 설정해줄 수 있습니다.
17.4 타입 서브스크립트
타입 서브스크립트는 인스턴스가 아니라 타입 자체에서 사용할 수 있는 서브스크립트입니다.
타입 서브스크립트를 구현하려면 정의할 때 static
키워드를 붙여주면 됩니다. 클래스의 경우에는 class
키워드를 사용할 수도 있습니다. 2
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