프로그래밍/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)