본문 바로가기
iOS/SWIFT

[Swift] Chapter 17. 서브스크립트

by 원만사 2021. 10. 10.
반응형

클래스, 구조체, 열거형에는 컬렉션, 리스트, 시퀀스 등 타입의 요소에 접근하는 단축 문법인 서브스크립트(Subscript)를 정의할 수 있다. 서브스크립트는 별도의 설정자(Setter) 또는 접근자(Getter)등의 메서드를 구현하지 않아도 인덱스를 통해 값을 설정하거나 가져올 수 있다.

클래스와 구조체는 필요한 만큼 얼마든지 서브스크립트를 구현할 수 있다. 서브스크립트를 여러 개 구현해도 외부에서 서브스크립트를 사용할 때는 서브스크립트를 사용할 때 전달한 값의 타입을 유추하여 적절한 서브스크립트를 선택하여 실행한다. 이렇게 여러 서브스크립트를 한 타입에 구현하는 것을 서브스크립트 중복 정의라고 한다.

매개변수의 타입과 반환 타입에는 제한이 없다. 함수와 마찬가지로 서브스크립트는 여러 개의 매개변수를 가질 수 있고, 매개변수 기본값을 가질 수 있다. 그렇지만 입출력 매개변수(in-out parameters)는 가질 수 없다.

 

17.1 서브스크립트 문법


서브스크립트는 인스턴스의 이름 뒤에 대괄호로 감싼 값을 써줌으로써 인스턴스 내부의 특정 값에 접근할 수 있다.

서브스크립트는 subscript 키워드를 사용하여 정의한다. 인스턴스 메서드와 비슷하게 매개변수의 개수, 타입, 반환 타입 등을 지정하며, 읽고 쓰기가 가능하도록 구현하거나 읽기 전용으로만 구현할 수 있다. 이는 접근자와 설정자를 사용할 수 있는 연산 프로퍼티의 형태와 유사하다.

서브스크립트를 정의하는 코드는 각 타입의 구현부 또는 타입의 익스텐션 구현부에 위치해야 한다.

 

/*
	코드 17-1. 서브스크립트 정의 문법
*/

subscript(index :Int) -> Int {
    get {
        // 적절한 서브스크립트 결괏값 반환
    }
    
    set(newValue) {
        // 적절한 설정자 역할 수행
    }
}

 

연산 프로퍼티와 마찬가지로 매개변수를 따로 명시해주지 않으면 설정자의 암시적 전달인자 newValue를 사용할 수 있다. 또, 연산 프로퍼티와 마찬가지로 읽기 전용 프로퍼티를 구현할 때는 get이나 set 키워드를 사용하지 않고 적절한 값만 반환해주는 형태로 구현해도 된다.

 

/*
	코드 17-2. 읽기 전용 서브스크립트 정의 문법
*/

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

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

 

17.2 서브스크립트 구현


서브스크립트는 자신이 가지는 시퀀스나 컬렉션, 리스트 등의 요소를 반환하고 설정할 때 주로 사용한다. 함수와 마찬가지로 서브스크립트는 여러 개의 매개변수를 가질 수 있고, 매개변수 기본값을 가질 수 있다. 그렇지만 입출력 매개변수는 가질 수 없다.

 

/*
	코드 17-3. School 클래스 서브스크립트 구현
*/

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")

let aStudent: Student? = highSchool[1]
print("\(aStudent?.number) \(aStudent?.name)") // Optional(1) Optional("JuHyun")
print(highSchool[]?.name) // 매개변수 기본 값 사용 : Optional("MiJeong")

[코드 17-3]에서는 서브스크립트의 index 매개변수가 매개변수 기본값을 0으로 갖지만 필요하지 않으면 매개변수 기본값이 없어도 상관없다.

 

17.3 복수 서브스크립트


하나의 타입이 여러 개의 서브스크립트를 가질 수도 있다. 다양한 매개변수 타입을 사용하여 서브스크립트를 구현하면 여러 용도로 서브스크립트를 사용할 수 있다는 뜻이다.

 

/*
	코드 17-4. 복수의 서브스크립트 구현
*/

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? {
        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
        }
    }
    
    // 세 번째 서브스크립트
    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")

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

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)
print(highSchool["SeongUk", 3]) // Optional(Student(name: "SeongUk", number: 3))
print(highSchool["HeeJin", 3]) // nil

 

첫 번째 서브스크립트는 학생의 번호를 전달받아 해당하는 학생이 있다면 Student 인스턴스를 반환하거나 특정 번호에 학생을 할당하는 서브스크립트이다. 두 번째 서브스크립트는 학생의 이름을 전달받아 해당하는 학생이 있다면 번호를 반환하거나 특정 이름의 학생을 해당 번호에 할당하는 서브스크립트이다. 마지막 세 번째 서브스크립트는 이름과 번호를 전달받아 해당하는 학생이 있다면 찾아서 Student 인스턴스를 반환한다.

 

17.4 타입 서브스크립트

이전까지 설명한 서브스크립트는 인스턴스에서 사용할 수 있는 서브스크립트이다. 타입 서브스크립트는 인스턴스가 아니라 타입 자체에서 사용할 수 있는 서브스크립트이다. 타입 서브스크립트를 구현하려면 서브스크립트를 정의할 때 subscript 키워드 앞에 static 키워드를 붙여주면 된다. 클래스의 경우에는 class 키워드를 사용할 수도 있다.

 

/*
	코드 17-5. 타입 서브스크립트 구현
*/

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

 

 

스위프트 프로그래밍: Swift 5 - 교보문고

객체지향, 함수형, 프로토콜 지향 패러다임까지 한 번에! | 스위프트를 제대로 이해하고 싶은 개발자를 위한 책스위프트는 iOS와 macOS용 애플리케이션 개발에 주로 사용하는 프로그래밍 언어입니

www.kyobobook.co.kr

 

반응형

댓글