키패스(keyPath) & 셀렉터(selector) & 메타 (Meta)

Ios_Roy·2023년 3월 5일
0

swift 문법

목록 보기
29/29
post-thumbnail

키패스(keyPath)

keyPath의 개념에 대한 이해

  • 키경로(문자열 처럼 쉽게 만들수 있는 인스턴스)로 속성에 쉽게 접근
class Dog {
    var name: String
    init(name: String) {
        self.name = name
    }
}

let dog1 = Dog(name: "초코")

dog1.name

"dog1.name"    // 이런식으로 접근하면 안될까?

// 위의 코드에서 굳이 필요성을 느끼지 못할 수 있지만,

class School {
    var name: String
    var affiliate: SmallSchool
    
    init(name: String, affiliate: SmallSchool) {
        self.name = name
        self.affiliate = affiliate
    }
}

class SmallSchool {
    var classMember: Person
    init(classMember: Person) {
        self.classMember = classMember
    }
}

class Person {
    var name: String
    init(name: String) {
        self.name = name
    }
}

// 예전방식으로 구현하려고 한다면========
//class Person: NSObject {
//    @objc var name: String
//    init(name: String) {
//        self.name = name
//    }
//}
// ==============================

let person1 = Person(name: "홍길동")
let smallSchool1 = SmallSchool(classMember: person1)
let school1 = School(name: "슈퍼고", affiliate: smallSchool1)

// 만약에 접근하기위해, 써야하는 코드가 늘어난다면...

let gildogsName = school1.affiliate.classMember.name

결론 - Swift5의 방식 (Swift5 - keyPath 표현식)

// 스위프트5의 방식

let namePath = \School.affiliate.classMember.name      // 미리 경로를 지정 (keyPath)

school1[keyPath: namePath]     // 딕셔너리방식(서브스크립트)로 접근

keyPath 타입 (외울 필요 없음)

  • AnyKeyPath : 어떤 속성인지 특정되지 않음(보통, 함수 파라미터형식으로만 사용)
  • PartialKeyPath : 타입에 대한 키패스(예를 들어 배열 같은 것으로 묶을때 사용)
  • KeyPath<Root, Value> : 타입과 (읽기)속성에 대한 키패스(구조체)
  • WritableKeyPath<Root, Value> : 타입과 읽기/쓰기 가능한 속성에 대한 키패스(구조체)
  • ReferenceWritableKeyPath<Root, Value> : 클래스의 타입과 읽기/쓰기 가능한 속성에 대한 키패스

예전 버전, 그리고 Objective-C의 방식 - #keyPath(타입.속성)방식

  • NSObject클래스를 상속해야함(구조체 지원 안함)
  • NSObject에 value(forKey:)메서드가 구현이 되어있기 때문
  • 속성에도 @objc를 붙여야함
  • 사용시, 구체적타입으로 다시 타입캐스팅해서 사용해야함
//let person2 = Person(name: "임꺽정")
//person2.name
//
//
//let gjName = person2.value(forKey: "name") as? String
//let gjName2 = person2.value(forKeyPath: #keyPath(Person.name)) as? String
//
//
//let path = #keyPath(Person.name)
//let gjName3 = person2.value(forKeyPath: path) as? String

셀렉터(selector)

#selector의 개념에 대한 이해

  • 클래스, Objective-C 프로토콜에 포함된 멤버에만 적용가능(구조체 적용 불가)
  • 내부적으로 Objective-C 프레임워크를 사용하고 있기 때문에
  • @objc 특성을 추가해야지만 사용가능
class Dog {
    var num = 1.0
    
    @objc var doubleNum: Double {
        get {
            return num * 2.0
        }
        set {
            num = newValue / 2.0
        }
    }
    
    @objc func run() {
        print("강아지가 달립니다.")
    }
}

//let selector = <#T##Selector#>

// 문법적인 약속
// (계산)속성을 가르킬때
let eyesSelector = #selector(getter: Dog.doubleNum)    // 계산(읽기) 속성
let nameSelector = #selector(setter: Dog.doubleNum)    // 계산(쓰기) 속성

// 메서드를 가르킬때
let runSelector = #selector(Dog.run)

메타 타입

  • 메타타입이란 타입의 타입이다. 클래스 타입의 타입과 구조체 타입의 타입, 열거형 타입, 프로토콜 타입의 타입도 메타타입이다.
  • 어떤 클래스 SomeClass 의 메타타입은 SomeClass.Type 이다.어떤 프로토콜 SomeProtocol 의 메타타입은 SomeProtocol.Protocol 이다.
  • 어떤 타입에 .self 를 붙이면 해당 타입을 어떤 값으로써 접근할 수 있다.예를들어, SomeClass.self 는 SomeClass의 인스턴스를 리턴하는게 아니라, SomeClass 라는 것 자체를 리턴한다(프로토콜도 마찬가지).
  • type(of:) 메소드는 인스턴스의 클래스를 리턴한다.
class Dog {
    static let species = "Dog"
    var name: String = ""
    var weight: Double = 0.0
}

       // ⬇︎ 붕어빵의 타입
let dog1: Dog = Dog()
               // ⬆︎ 붕어빵

dog1.name = "초코"
dog1.weight = 10.0

let dog2: Dog = Dog()
dog2.name = "보리"
dog2.weight = 15.0
// ⬇︎ 붕어빵틀의 타입
let dog: Dog.Type = Dog.self
                    // ⬆︎ 붕어빵틀

let dogSelf: Dog.Type = type(of: dog1)    // 붕어빵틀

Dog.species     // "Dog"
Dog.self.species    // "Dog"

class Person {
    static let species = "인간"
    var name: String = ""
}

// 인스턴스의 타입과 인스턴스
let person1: Person = Person()
person1.name = "홍길동"

// 인스턴스의 타입과 인스턴스
let person2: Person = Person()
person2.name = "임꺽정"
// 붕어빵틀의 타입   // 붕어빵틀의 메모리
let pSelf1: Person.Type = Person.self
let pSelf2: Person.Type = type(of: person1)   // 타입의 타입 (붕어빵틀형식으로 메모리에 있어)

pSelf1.species  // "인간"
pSelf2.species  // "인간"
Person.species  // "인간"
Person.self.species // "인간"

var num1: Int = 5  // 붕어빵
var num2: Int = 7

// (타입자체의) 타입속성
Int.max
Int.self.max

Int.zero
Int.self.zero

var numberSelf: Int.Type = Int.self

type(of: person1)
Person.self

/*:
 ---
 * 메타타입을 선언하는 방법
 ---
 */
/**======================

메타타입을 선언하는 방법

[커스텀타입의 경우]

  • 클래스이름.Type
  • 구조체이름.Type
  • 열거형이름.Type

[프로토콜의 경우]

  • 프로토콜이름.Protocol

메타타입을 사용하는 API

  • 1) 테이블뷰셀을 등록하는 경우에 메타타입을 사용
tableView.register(<#T##cellClass: AnyClass?##AnyClass?#>, forCellReuseIdentifier: <#T##String#>)
  • 2) JSONDecoder 객체를 사용해서 디코드메서드 사용시
try? decoder.decode(<#T##type: Decodable.Protocol##Decodable.Protocol#>, from: <#T##Data#>)

@available / #available

Swift API Availability

  • 새로운 업데이트 사항에 따라서, iOS 예전버전에서는 코드로 인해 크래시가 발생할 수 있음
  • 그래서 해당 코드에 대해 어떤 버전이상만 적용가능하다는 힌트를 컴파일러에 주는 것
  • @available은 타입 또는 메서드에 붙일때
  • #available는 조건문에서 사용

(참고) iOS 등버전, Swift버전에 따라

  • iOS
  • iOSApplicationExtension
  • macOS
  • macOSApplicationExtension
  • watchOS
  • watchOSApplicationExtension
  • tvOS
  • tvOSApplicationExtension
  • swift
@available(iOS 11.0, *)
class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
    }
    
    @available(iOS 11.0, *)
    func doSomething() {
        // (if, while, guard문으로도 사용 가능)
        if #available(iOS 11.0, *) {
            // iOS 11버전이상인 경우 적용 시킬 코드를 작성
            
        } else {
            // iOS 11버전미만인 경우 적용 시킬 코드를 작성
        }
        
    }

}
profile
iOS 개발자 공부하는 Roy

0개의 댓글