Swift Type metadata

17042 단어 SwiftSwiftCompiler
Swift는 실행 시 정보를 보존하는 메커니즘Type metadata이 있습니다.자주 사용하는 것은 아니지만 Swift의 작동 시간을 이해하는 데 중요한 요소입니다.
이 글은 컴파일러의 코드와 문서에서 찾아낸 Type metadata를 간단하게 설명한다.

Type metadata란


Swift 실행 시 유지되는 유형 정보입니다.비상규class,struct,enum는 컴파일할 때 정적이지만 비상규형과 함수형, 원조 등non-nominal형은 실행할 때 동적으로 제작된다.
이거Type metadata에서
  • 가 이 유형의 유형enum인지 struct
  • 인지
  • class(조작 유형에 사용되는 함수 그룹)
  • 유형 매개 변수
  • 모듈 라벨
  • 등의 메시지를 남겼다.금형의 종류에 따라 배치도 대폭 다르기 때문에 하나하나 읽는 것을 추천합니다문서.

    Swift에서 ValueWitnessTable 처리


    그러면 Type metadata를 사용하여 Swift에서 클래스 실례 크기를 얻으세요.Type metadata에서 목표 정보를 얻으려면 이 정보가 메타데이터의 메모리 레이아웃에 있는 위치를 알아야 한다.참조swift/TypeMetadata.rst 재현 메모리 레이아웃은 다음과 같습니다.
    struct ClassMetadata {
        let isaPointer: Int
        let superClass: Any.Type
        let objcRuntimeReserved1: Int
        let objcRuntimeReserved2: Int
        let rodataPointer: Int
        let classFlags: Int32
        let instanceAddressPoint: Int32
        let instanceSize: Int32
    }
    
    그럼, 메모리의 표현을 알았으니까Type metadata 실제로 시도해 보세요.
    SwiftType metadata는 이 유형의 인스턴스Metatype에 대한 포인터입니다.일단 Type metadata로 포인터형 배역을 해보자.
    class Cat {}
    let catType: Cat.Type = Cat.self
    MemoryLayout.size(ofValue: Cat.self) // 8
    let metadataPointer = unsafeBitCast(catType, to: UnsafePointer<ClassMetadata>.self)
    let metadata = metadataPointer.pointee
    print(metadata.instanceSize) // 16
    
    필요한 인스턴스 크기 확보
    그러나 이 방법으로unsafeBitCast의 메타데이터를 얻으려 해도 순조롭지 않다.
    struct Stone {}
    let stoneType: Stone.Type = Stone.self
    MemoryLayout.size(ofValue: Stone.self) // 0
    let metadataPointer = unsafeBitCast(stoneType, to: UnsafePointer<StructMetadata>.self)
    let metadata = metadataPointer.pointee // Crash! :bomb: 
    
    제대로 사용하려면struct 조금 더 알아야 할 것 같아요.

    Thin Metatype

    Type metadata의 원형 사이즈가 0이 된 원인을 찾기 위해 원형 컴파일러를 만드는 코드를 읽으십시오.
    swift/MetadataRequest.cpp
    /// Emit a metatype value for a known type.
    void irgen::emitMetatypeRef(IRGenFunction &IGF, CanMetatypeType type,
                                Explosion &explosion) {
      switch (type->getRepresentation()) {
      case MetatypeRepresentation::Thin:
        // Thin types have a trivial representation.
        break;
    
      case MetatypeRepresentation::Thick:
        explosion.add(IGF.emitTypeMetadataRef(type.getInstanceType()));
        break;
    
      case MetatypeRepresentation::ObjC:
        explosion.add(emitClassHeapMetadataRef(IGF, type.getInstanceType(),
                                               MetadataValueType::ObjCClass,
                                               MetadataState::Complete));
        break;
      }
    }
    
    유형의 표현 방법이 struct일 때 운행 시간 정보를 출력하지 않습니다.그럼, 무엇이 있습니까?
    swift/Types.h
    enum class MetatypeRepresentation : char {
      /// A thin metatype requires no runtime information, because the
      /// type itself provides no dynamic behavior.
      ///
      /// Struct and enum metatypes are thin, because dispatch to static
      /// struct and enum members is completely static.
      Thin,
      /// A thick metatype refers to a complete metatype representation
      /// that allows introspection and dynamic dispatch. 
      ///
      /// Thick metatypes are used for class and existential metatypes,
      /// which permit dynamic behavior.
      Thick,
      /// An Objective-C metatype refers to an Objective-C class object.
      ObjC
    };
    
    스위프트의 메타 유형은 Thick, Thin, ObjC 세 가지로 나뉜다.
  • Thin
  • MetatypeRepresentation::Thin, MetatypeRepresentation 등 정적 결정 행위의 유형
  • Thick
  • struct 등 동적 행위의 유형
  • ObjC
  • Objective-C의 유형
  • Thin형은 정태적으로 행동거지를 결정하기 때문에 운행 시간 정보가 필요 없고 원형의 사이즈는 0이다.이것은 enum원 유형을 얻는 데 실패한 이유다.
    protocol Animal {
        static func kind() -> String
    }
    struct Cat {
        static func kind() -> String {
            return "猫"
        }
    }
    
    let catType: Cat.Type = Cat.self // Thin
    let animalType: Animal.Type = Cat.self // Thick
    animalType.kind() // 猫
    
    다른 한편, 상기 예에서 보듯이 원 유형의 하위 유형을 통해 Thin형을 Thick형에 대입할 수 있다.
    또한 class의 정확한 조작으로 인해 Existential의 원형에는 실제 원형이 보존되어 있다.Existential Metatype을 통해서도 씽형struct을 받을 수 있다는 것이다.

    Existential Metatype container


    Existential Metatype container는 Existential Metatype이 실행될 때 표현되는 것으로 Existential container의 메타데이터 버전과 유사합니다.내부에는 실제 유형의 실례 animalType.kind() 와 위니스 테이블을 가리키는 바늘이 저장되어 있습니다.
    swift/GenExistential.cpp
    struct ExistentialMetatypeContainer<Metadata> {
        let metadata: UnsafePointer<Metadata>
        let witnessTable: UnsafePointer<WitnessTable>
    }
    
    방금 실패한 Type metadata 메타데이터를 가져오려면 이것을 사용하십시오.
    struct StructMetadata {
        let kind: Int
    }
    protocol Animal {}
    struct Cat: Animal {}
    
    let animalType: Animal.Type = Cat.self
    let metatypeContainer = unsafeBitCast(animalType, to: ExistentialMetatypeContainer<StructMetadata>.self)
    metatypeContainer.metadataPointer.pointee.kind // 1
    
    왜냐하면 struct metadata의kind는 1이니까. 목적을 순조롭게 얻었다Type metadata.
    하지만 이 방법에는 문제가 하나 있다.일부 프로토콜을 준수하지 않으면 메타데이터를 얻으려는 Thin형을 Existential Metatype container로 압축할 수 없습니다.

    Any.Type


    그러나 우리는 모든 종류의supertypestruct을 가지고 있다.게다가 애니는 non-nominal이기 때문에 Thick형으로 사용하고 운행시간 정보를 유지했다.
    struct Stone {}
    let stoneType: Any.Type = Stone.self
    let metadataPointer = unsafeBitCast(stoneType, to: UnsafePointer<StructMetadata>.self)
    let metadata = metadataPointer.pointee
    metadata.kind // 1
    
    이렇게 안전(?)Type metadata 획득 방법 설정

    총결산

    Any는 실제로Server Side Swift의 프레임 Zewo 등에 사용 Swift 언어 기능을 확장할 수 있는 환상적인 정보원이다.다른 한편, 문서를 만들지 못한 부분이 많아 수정할 수도 있다.사용 시 테스트 등 대책을 꼼꼼히 써야 한다.
    신나게 Type metadata 놀아라.

    참고 자료

  • swift/TypeMetadata.rst · apple/swift
  • 좋은 웹페이지 즐겨찾기