티스토리 뷰

프로그래밍/Swift

Swift 기초 공부4

시르베어 2025. 2. 2. 22:45

서브스크립트

  • 타입요소에 접근하는 단축 문법
  • 인덱스를 통해 설정하거나 가져올 수 있음
  • 읽고 쓰기가 가능하도록 구현하거나 읽기 전용으로 구현할 수 있음
subscript(index: Int) -> Int {
    get {
        // 접근자
    }
    set(newValue) {
        // 설정자
    }
}
class Human {
    var name: String = ""
    var family: [String] = []

    subscript(index: Int) -> String? {
        get { // 하나만 있으면 자동으로 get으로 인식(읽기전용)
            if index < self.family.count {
                return self.family[index]
            }
            return nil
        }
    }
}

타입 서브스크립트

  • subscript 키워드 앞에 static 키워드를 붙여주면됨 (클래스의 경우 class키워드를 붙여도됨 - 재정의관련)
enum School: Int {
    case elementary = 1, middle, high
    static subscript(level: Int) -> School? {
        return Self(rawValue: level)
    }
}
let school: School? = School[1] // elementary

상속

  • 재정의 : 자식 클래스가 부모클래스의 요소들을 새롭게 작성
  • 자식클래스에서 부모클래스의 것을 사용하고 싶다면 super 프로퍼티를 사용
class A {
    var element: T
    func method() { }
}
class B: A { // B는 A를 상속
    override func method() { } // 재정의
}

프로퍼티 감시자 재정의

  • 프로퍼티 접근자와 감시자는 동시에 재정의 불가 (둘다 원하면 재정의하는 접근자에 프로퍼티 감시자의 역할을 구현해야함)
class Human {
    var name: String = ""
    var age: Int = 0
    var koreanAge: Int {
        get { return self.age+1 }
        set { self.age = newValue }
    }
    var fullName: String {
        get { return self.name }
        set { self.name = newValue }
    }
}
class NPC: Human {
    override var koreanAge: Int {
        get { return super.koreanAge }
        set { self.age = newValue - 1 }
        didSet { } // 오류 발생
    }
    override var fullName: String { // 부모에 프로퍼티 접근자, 자식에 프로퍼티 감시자
        didSet { print("Full Name : \\(self.fullName)") }
    }
}

서브스크립트 재정의

  • 매개변수와 반환 타입이 같아야함 (하나라도 다르면 재정의가 아닌 별개의 서브스크립트로 처리)

재정의 방지

  • final 키워드를 명시하면 재정의를 방지 할 수 있음
  • ex. final var, final func, final class, final subscript

타입캐스팅

  • 인스턴스의 타입을 확인하거나 자신을 다른 타입의 인스턴스인양 행세할 수 있는 방법으로 사용
  • is와 as 연산자로 구현
  • 값의 타입을 확인하거나 다른 타입으로 전환 가능, 프로토콜을 준수하는지도 확인 가능
  • 타입 캐스팅도 실패할 수 있기 때문에 as의 경우 as?와 as!로 나뉨 (옵셔널 참고)
var num1: Int = 10
print(num1 is Int) // true

class Person {
    var name: String
    init(name: String) {
        self.name = name
    }
}
class NPC : Person { }
let A: Person = Person(name: "A")
let B: NPC = NPC(name: "B"

print(A is Person) // true
print(A is NPC) // false
print(B is Person) // true
print(B is NPC) // true
if let C: Person = B as? Person { // B는 Person으로 타입 캐스팅 가능
    print("B는 Person이 될 수 있음")
}
if let D: NPC = A as? NPC {
    print("A는 NPC가 될 수 있음") // A는 NPC로 타입 캐스팅 불가
}

프로토콜

  • 특정 역할을 하기 위한 메서드, 프로퍼티, 기타 요구사항 들의 청사진
  • 스스로 기능을 구현하지는 않음
protocol 프로토콜이름 { 프로토콜 정의 }
struct A: AProtocol, BProtocol { }
class B: AProtocol, BProtocol { }
enum C: AProtocol, BProtocol { }

프로퍼티 요구

  • 프로퍼티의 종류(연산프로퍼티, 저장 프로퍼티 등)는 따로 신경쓰지 않음
  • 프로토콜은 최소한의 것을 제공하기 때문에 읽기용으로 작성되어있다면 읽기+쓰기로 만들어도 상관없음 (읽기+쓰기로 작성되어 있다면 읽기로만 만들수는 없음)
protocol A {
    var AProperty: String { get set } // 읽고 쓰기
    var BProperty: Int { get } // 읽기용
}

메서드 요구

  • 프로토콜 메서드 요구에서는 매개변수의 기본값을 지정할 수 없음
  • 타입 메서드의 경우 static 키워드를 명시 (실제 구현에서 static나 class 중 아무거나 사용해도됨)
  • 프로토콜에서 자료형이 Any면 채택한 class에서도 Any여야 함(Any라고 해서 다른 자료형으로 바꾸면 안됨)
protocol A {
    func method1(_ data: Any)
    static func method2(_ data: Any)
}
class Instance: A {
    func method1(_ data: Any) { print(data) }
    class func method2(_ data: Any) { print(data) }
}
let B: Instance = Instance()
B.method1("method")
Instance.method2("Static")

가변 메서드 요구

  • 값 타입(구조체, 열거형 등)의 인스턴스 메서드에서 내부의 값을 변경하고자 하면 func 앞에 mutating 키워드를 명시해줘야함
  • 프로토콜에서 mutating을 명시해 주고 class(참조 타입)에서 해당 프로토콜을 채택한다면 class 파트에서는 mutating을 안적어도 됨
protocol A {
    var value: Int { get }
    mutating func add(_ num: Int)
}
struct B: A {
    var value: Int = 0
    mutating func add(_ num: Int) { self.value += num }
}
class C: A {
    var value: Int = 0
    func add(_ num: Int) { self.value += num }
}

이니셜라이저 요구

  • 프로퍼티, 메서드와 마찬가지로 이니셜라이저도 요구 가능
  • 클래스 타입에서 이니셜라이저 요구에 따라 구현할 때는 required 식별자를 붙인 요구 이니셜라이저로 구현해야함
protocol A {
    var value: String { get }
    init(value: String)
}
struct B: A {
    var value: String
    init(value: String) { self.value = value }
}
class C: A {
    var value: String
    required init(value: String) { self.value = value }
}

프로토콜 상속과 클래스 전용 프로토콜

  • 프로토콜은 하나 이상의 프로토콜을 상속받을 수 있고 다중 채택도 가능하다
  • 프로토콜의 상속 리스트에 class 키워드(AnyObject)를 추가하면 클래스 타입에만 채택 가능
  • is (타입 캐스팅 참고)을 사용하면 어떤 프로토콜을 채택했는지 확인 가능
protocol A {
    func method1()
}
protocol B: A {
    func method2()
}
protocol C {
    func method3()
}
protocol D: AnyObject, B, C {
}
class E: D {
    func method1(){ }
    func method2(){ }
    func method3(){ }
}
struct F: D{ // 에러발생
    func method1(){ }
    func method2(){ }
    func method3(){ }
}
let value: E = E()
print(value is C)

'프로그래밍 > Swift' 카테고리의 다른 글

class 키워드와 AnyObject 키워드  (0) 2025.02.02
Swift 기초 공부5  (0) 2025.02.02
Swift 기초 공부3  (0) 2025.02.02
Swift 기초 공부2  (0) 2025.01.30
Swift 기초 공부1  (0) 2025.01.27
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/01   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함