Thumbs up comments, feel useful friends can pay attention to the author public number iOS growth refers to north, continue to update

Despite its emphasis on static typing, compile-time security, and static scheduling, Swift still provides a reflection mechanism in its standard library. Of course, it is often claimed that reflection is not really used in Swift.

Reflection is a common programming language feature that enables us to dynamically examine and process the members of a type at run time. This seems to go against Swift’s strong focus on compile-time validation, and while Swift’s implementation of reflection is certainly more limited than in your other languages, it’s been around since day one. The Mirror API allows developers to check and manipulate arbitrary values at run time.

The Reflection feature in the Swift version allows us to iterate over all the stored properties that a type has (whether structure, class, or any other type) and read their values, enabling a kind of meta-programming that allows us to write code that actually interacts with the code itself.

Reflection in Swift is read-only and cannot modify any properties. But it’s still pretty powerful.

Referring to wikipedia’s definition of reflection in computer science, we can use the new term Introspection to describe reflection in Swift — a method of looking at objects’ properties without modifying them is called Introspection.

But the term is still launch, even if he is introspective.

Learning goals

Through the completion of this study, we mainly solve the following problems

  1. What exactly is a Mirror? What are the ways to use it?
  2. What can I do with this limited reflection in a real project?
  3. Briefly describes theMirrorThe implementation principle ofHow Mirror Work?

Mirror

On the developer website, Mirror exists in the Debugging and Reflection function for querying runtime values.

A representation of the substructure and display style of an instance of any type.

Represents the substructure and display style of any type instance

PS: When looking for tools we don’t know about on the developer website, I recommend at least completing the Overview section.

struct Point {
    let x: Int, y: Int
}

let p = Point(x: 21, y: 30)
print(String(reflecting: p))
/ / Prints "▿ Point
// - x: 21
// - y: 30"
Copy the code

Create a Mirror instance

Mirror is a structure that provides a constructor public init(reflecting: Any). Now we use that constructor to generate an instance of Mirror and print the instance object

let names = ["Zaphod"."Slartibartfast"."Trillian"."Ford"."Arthur"."Marvin"]
let nameMirror = Mirror(reflecting: names)
print(nameMirror)
//Mirror for Array<String>
Copy the code

NameMirror is a Names Array — or, more accurately, a mirror image of Array

.

As defined by its default constructor public init(reflecting subject: Any), its subject is an Any. As the most common type in Swift, probably all instances in Swift follow this type, which makes this Mirror compatible with Struct, Class, Enum, Tuple, Array, Dictionary, Set, and almost any other type.

In effect, Any is an empty protocol that all types implicitly follow.

What is a Mirror

Mirror Struct Mirror Struct Mirror Struct

public struct Mirror {

    public enum AncestorRepresentation {
        /// Generates a default mirror for all ancestor classes.
        /// This case is the default when initializing a `Mirror` instance.
        case generated
        /// Uses the nearest ancestor's implementation of `customMirror` to create
        /// a mirror for that ancestor.
        case customized(() -> Mirror)
      /// Suppresses the representation of all ancestor classes.
        ///
        /// In a mirror created with this ancestor representation, the
        /// `superclassMirror` property is `nil`.
        case suppressed
    }

    public init(reflecting subject: Any)

    public typealias Child = (label: String? , value:Any)

    public typealias Children = AnyCollection<Mirror.Child>

    public enum DisplayStyle {
        case `struct`
        case `class`
        case `enum`
        case tuple
        case optional
        case collection
        case dictionary
        case set
        
        .
    }
    .

    public let subjectType: Any.Type

    public let children: Mirror.Children
  
    public let displayStyle: Mirror.DisplayStyle?

    /// A mirror of the subject's superclass, if one exists.
    public var superclassMirror: Mirror? { get}}Copy the code
  • DisplayStyle enumeration, which lists the types of representations supported by the Mirror. Previously we said that Mirror is compatible with almost all types in Swift, so what are the exceptions? If you read my Swift in 100 Days in detail, you’ll notice that it doesn’t define functions/closures — a Closure is a special type of function.

  • The Mirror defines a TypeAlias Child to decorate the Child elements of its existing structure.

     public typealias Child = (label: String? , value:Any)
    Copy the code

    Each child element has an optional label and a value of type Any. Not all structures supported by reflection have substructures with specific names. Struct/Class takes the name of the attribute as the label, but only the index, not the name, is in the collection. Tuples are a bit special though, we can define tags for tuples — but this is not necessary, so in Swift tuples are mirrored with Optional(“.0”), Optional(“.1”)…

  • AncestorRepresentation This enumeration is used to define the superclass that reflects the reflected Subject. That is, this applies only to subjects of type Class. By default, Swift generates an additional image for each superclass. However, if you need more flexibility, you can use the AncestorRepresentation enumeration to define how the superclass is reflected.

Using Mirror in Swift

We now have a brief overview of the Mirror’s important properties, methods, and enumerations. I’ll go through a few examples of how these things can be used.

  • displayStyle: Mirror.DisplayStyle? : Subject display style
  • Let subjectType: any. Type: The Type of the subject
  • Children of let children: children: subject
  • func superclassMirror() -> Mirror? : mirror of the Subject superclass
DisplayStyle

We mentioned above about the type not included in the DisplayStyle enumeration — functions/closures. What happens if we create a Mirror for the function/closure and print its DisplayStyle?

let names = ["Zaphod"."Slartibartfast"."Trillian"."Ford"."Arthur"."Marvin"]
let nameMirror = Mirror(reflecting: names)
print(nameMirror) //Mirror for Array<String>
print(nameMirror.displayStyle as Any) //Optional(Swift.Mirror.DisplayStyle.collection)

let closure = { (a: Int) - >Int in return a * 2 }
let closureMirror = Mirror(reflecting: closure)
print(closureMirror) //Mirror for (Int) -> Int
print(closureMirror.displayStyle as Any) //nil

func greetUser(name: String) {
    print("Hello,\(name)!")}let squaredMirror = Mirror(reflecting: greetUser)
print(squaredMirror) //Mirror for (String) -> ()
print(squaredMirror.displayStyle as Any) //nil
Copy the code

Although you can get a mirror image, the function/closure display style is nil.

SubjectType

Static type of the reflected subject.

print(closureMirror.subjectType)
// (Int) -> Int
print(squaredMirror.subjectType)
//(String) -> ()
print(Mirror(reflecting: (1.2."3")).subjectType)
// (Int, Int, String)
print(Mirror(reflecting: 5).subjectType)
// Int
print(Mirror(reflecting: "test").subjectType)
// String
print(Mirror(reflecting: NSNull()).subjectType)
// NSNull
Copy the code

It is worth noting that the Apple documentation includes the sentence: When this Mirror is a Mirror of another Mirror’s superclass, the type may be different from the subject’s dynamic type.

The two points pointed out here are possible and dynamic typing. The author does not quite understand to this, hope big guy gives advice.

Children

A collection of child elements that describe the structure of the reflected Subject. This is probably the most commonly used function to get a collection of current instance attributes to represent its structure.

Let’s use the following example to help us understand its structure

class Vehicle {
    var wheels:Int = 0
    var maxSpeed:Int = 0

    func drive(a) {
        print("This vehicle is driving!")}deinit {
        print("This vehicle is all!")}}class RaceCar: Vehicle {
    var hasSpoiler = true
    var accessories = Accessories(a)var competitionTypes = ["Road race"."Cross country"."Rally"]
    
    override func drive(a) {
        print("VROOOOM!!!")}}struct Accessories {
    var material = "leather"
    var color = "red"
    var type: Use = .char
    var price = 100
    
    enum Use {
        case char
        case speaker
        case steeringWheel
    }
}
Copy the code

When we create a Racecar-based instance, we use the children property to get the structure of the instance

let ferrari = RaceCar(a)let ferrariMirror = Mirror(reflecting: ferrari)
for case let (label?, value) in ferrariMirror.children {
    print (label, value)
}
//hasSpoiler true
//accessories Accessories(material: "leather", color: "red", type: __lldb_expr_3.Accessories.Use.char, price: 100)
//competitionTypes ["Road race", "Cross country", "Rally"]

for property in ferrariMirror.children {
    print("name: \(String(describing: property.label)) type: \ [type(of: property.value))")}//name: Optional("hasSpoiler") type: Bool
//name: Optional("accessories") type: Accessories
//name: Optional("competitionTypes") type: Array<String>
Copy the code

This is easy to find, we can get the property name, property type, and property value.

extended

In our early documentation section, we learned that the implementation of the children property is based on an alias

typealias Mirror.Children = AnyCollection<Mirror.Child>
.
public let children: Mirror.Children
Copy the code

This is a value of type AnyCollection. AnyCollection is a Type-erased package for all collections that have indexes that support forward traversal.

Sometimes we can do something with this AnyCollection, like print some properties?

if let b = AnyBidirectionalCollection(ferrariMirror.children) {
    for element in b.suffix(5) {
        print(element)
    }
}
//(label: Optional("hasSpoiler"), value: true)
//(label: Optional("accessories"), value: __lldb_expr_15.Accessories(material: "leather", color: "red", type: __lldb_expr_15.Accessories.Use.char, price: 100))
//(label: Optional("competitionTypes"), value: ["Road race", "Cross country", "Rally"])
Copy the code

Here we print five properties of the current structure before we print them — but only three properties of our instance will be printed.

There is also a type-erased concept that can be hard for you to understand. This is a somewhat fancy concept that converts an association type to a generic constraint. Any of our Swift… Most of the standard libraries in the series implement this functionality. If you have expectations, please contact me. I mentioned the concept of relational types in my Swift in 100 Days scope and Generics book, which may help you.

superclassMirror

In the above example, we can only see the value of the current Subject property, and our RaceCar inherits from the Vehicle object.

public var superclassMirror: Mirror? { get }
Copy the code

If the object is not class-based, the result is an optional value, otherwise it is a new Mirror instance.

print(ferrariMirror.superclassMirror as Any)

for property in ferrariMirror.superclassMirror!.children {
    print("name: \(String(describing: property.label)) type: \ [type(of: property.value))")}//Optional(Mirror for Vehicle)
//name: Optional("wheels") type: Int
//name: Optional("maxSpeed") type: Int
Copy the code

We can use this to determine whether the instance has a superclass and its superclass properties.

Use Case

Any feature or concept that is not in actual use is just talk on paper. Just print the properties of the instance – sounds exciting, but you can’t change it!! So what other use cases do we have besides letting us check the value of the instance? Let’s explain them one by one.

Perform the same operation automatically

Let’s say the APP we’re developing has a UserSession class that tracks part of what the user does after logging in. For example, storage classes such as user basics, user likes/favorites, and user Settings — we generate these storage classes for convenience when calling them. As a single-session application, we need to reset these storage classes when the user logs out.

// Create a generic reset method
protocol Resettable {
    func reset(a)
}

extension UserInfoStorage: Resettable { . }
extension FavoritesStorage: Resettable { . }
extension SettingsStorage: Resettable {.}

class UserSession {
    var userInfo: UserInfoStorage
    var favorites: FavoritesStorage
    var settings: SettingsStorage
    .
}
Copy the code

When our user logs out, we reset our message:

extension UserSession {
    func logOut(a) {
        userInfo.reset()
        favorites.reset()
        settings.reset()
    }
}
Copy the code

As many storage class information as our session needs to store, we need to perform the reset() method in the logOut() method. So is there an elegant way to do this?

As some students mentioned, by iterating through all the properties of the UserSession, you can have that property call the reset() method when it conforms to the Resettable protocol. Ok, so how do you iterate through all the attributes of UserSession?

In OC we can get the property list of the class. But what about in Swift? That’s right. Children using Mirror.

func logOut(a) {
  let mirror = Mirror(reflecting: self)
  for child in mirror.children {
    if let resettable = child.value as? Resettable {
      resettable.reset()
    }
  }
}
Copy the code

Now, if we add a new Resettable attribute to UserSession, reset() will be called automatically after logOut() is called.

This is a good approach when we come across properties that need to perform an indefinite number of traversals but have the same method.

The above method is specific to the reset() method and requires the Resettable attribute. We can further extend it to support the execution of class-specific methods that do not require parametric methods. For those unfamiliar with generics, please refer to my previous article on generics and scope.

extension Mirror {
    static func reflectProperties<T> (of target: Any.matchingType type: T.Type = T.self.using closure: (T) - >Void
    ) {
        let mirror = Mirror(reflecting: target)

        for child in mirror.children {
            (child.value as? T).map(closure)
        }
    }
}
Copy the code

Now we can modify our logOut() method and allow it to be called when we need a similar scenario

extension UserSession {
    func logOut(a) {
        Mirror.reflectProperties(of: self) {
            (child: Resettable) in
            child.reset()
        }
    }
}
Copy the code

Use CustomReflectable for debugging

You can use CustomReflectable to help you debug. If we are not happy with the default provided type, we can make it conform to CustomReflectable and return a custom mirror instance.

As a very common LLDB command, Po:

(lldb) po self

<Landmark.ViewController: 0x7f96a643e0d0>
Copy the code

Let’s expand our UIViewController

extension UIViewController: CustomReflectable {
    public var customMirror: Mirror {
        let children = KeyValuePairs<String.Any>(dictionaryLiteral:("title", title!))
        return Mirror(NSUserActivity.self, children: children, displayStyle: .class, ancestorRepresentation: .suppressed)
    }
}
Copy the code

Then we click on the Po command again

(LLDB) Po self ▿ <Landmark.ViewController: 0x7f96a643e0d0> - title: Turtle RockCopy the code

One thing to note is that the title property needs to be assigned.

More and more

While looking for reflection in Swift, I found a Github repository called Wrap that automatically encodes JSON using reflection — converting objects to JSON might help.

How Mirror Work?

As an open source language, we can understand the implementation principle through Swift source code. This article is based on Swift 5.3.3 release code. If there are updates, I will follow up. Now that the ABI is stable, theoretically there should not be much volatility. How Mirror Works (Swift.org)

The code structure

The reflection API is partially implemented in Swift and partially implemented in C++. Swift is better suited for implementing more Swift interfaces and makes many tasks easier. The bottom layer of the Swift runtime is implemented using C++. The Swift implementation of reflection is in reflectionmirror. Swift, and its C++ implementation file is reflectionmirror.cpp.

Note that in the new version of the code there is a reflectionMirrorObjc.mm which is a different treatment of objective-C classes.

The two communicate via a small set of C++ functions exposed to Swift. Instead of using the SWIFt-generated C bridge layer, these functions are declared directly in Swift as specified custom symbols, and the C++ functions with these names are specifically implemented in a way that can be called directly by Swift. These two parts of code can interact without caring about how the bridging mechanism handles passing values behind the scenes, but you still need to know exactly how Swift should pass parameters and return values.

For example, let’s look at the _getChildCount function in reflectionMirror. swift:

@_silgen_name("swift_reflectionMirror_count")
internal func _getChildCount<T> (_: T.type: Any.Type) -> Int
Copy the code

The @_silgen_name modifier tells the Swift compiler to map this function to the swift_reflectionMirror_count symbol instead of the _getChildCount method name modifier that Swift normally corresponds to. Note that the leading underscore indicates that the modifier is reserved in the standard library. On the C++ side, the function looks like this:

SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
intptr_t swift_reflectionMirror_count(OpaqueValue *value,
                                      const Metadata *type,
                                      const Metadata *T) {
Copy the code

SWIFT_CC(swift) tells the compiler that this function uses the Swift calling convention, not C/C++. SWIFT_RUNTIME_STDLIB_INTERFACE flag This is a function that is part of the interface on the Swift side, and it is also labeled extern “C” to avoid C++ method name modifiers and ensure that it has the expected symbol on the Swift side. At the same time, C++ arguments are deliberately matched to function calls declared in Swift. When Swift calls _getChildCount, C++ calls the function with the value of the pointer to the Swift value, the type of the type argument, and the function argument T of the corresponding type stereotype

.

Dynamic dispatch

All of these functions need to be distributed in different implementation code because they require different types of checks. This sounds a bit like dynamic method dispatch, except that choosing which implementation to call is more complicated than checking the method used by the object type. The reflection code attempts to simplify the use of abstract base classes with interfaces containing C++ version information, and a large number of subclasses containing various cases for C++ dynamic distribution. A single function maps a Swift type to an instance of one of its C++ classes. Calls a method on an instance and dispatches the appropriate implementation.

The mapped function is called call and is declared as follows:

template<typename F>
auto call(OpaqueValue *passedValue, const Metadata *T, const Metadata *passedType,
          const F &f) -> decltype(f(nullptr)){... }Copy the code

PassedValue is a pointer to the value of the Swift that is actually passed in. T is the static type of the value, corresponding to the paradigm parameter

in Swift. PassedType is the type that is explicitly passed to the Swift side and is actually applied during reflection (this type is different from the object type that is actually run when using Mirror as the parent instance). Finally, the f argument passes an object reference to the implementation that the function finds to be called. The function then returns the value that was returned when the f argument was called, making it easier for the user to get the return value.

The important thing is that it ends the call to F with a subclass instance of ReflectionMirrorImpl, and then calls the methods on that instance to get the real work done.

This is the abstract base class ReflectionMirrorImpl based on reflection implementation

// Abstract base class for reflection implementations.
struct ReflectionMirrorImpl {
  const Metadata *type;
  OpaqueValue *value;
  
  virtual char displayStyle(a) = 0;
  virtual intptr_t count(a) = 0;
  virtual intptr_t childOffset(intptr_t index) = 0;
  virtual const FieldType childMetadata(intptr_t index,
                                        const char **outName,
                                        void (**outFreeFunc)(const char *)) = 0;
  virtual AnyReturn subscript(intptr_t index, const char **outName,
                              void (**outFreeFunc)(const char *)) = 0;
  virtual const char *enumCaseName(a) { return nullptr; }

#if SWIFT_OBJC_INTEROP
  virtual id quickLookObject(a) { return nil; }
#endif
  
  // For class types, traverse through superclasses when providing field
  // information. The base implementations call through to their local-only
  // counterparts.
  virtual intptr_t recursiveCount(a) {
    return count(a); }virtual intptr_t recursiveChildOffset(intptr_t index) {
    return childOffset(index);
  }
  virtual const FieldType recursiveChildMetadata(intptr_t index,
                                                 const char **outName,
                                                 void (**outFreeFunc)(const char *))
  {
    return childMetadata(index, outName, outFreeFunc);
  }

  virtual ~ReflectionMirrorImpl() {}};Copy the code

Realization of reflection

When we need reflection, the interface function between Swift and the C++ component calls the corresponding method with call.

Let’s focus on getting the Children property value and look at the implementation of the launch in Swift.

In the Mirror document, Children are defined this way

public typealias Children = AnyCollection<Mirror.Child> 
Copy the code

In the reflectionMirror. swift file, we see that the implementation is actually based on this section

self._children = _Children(
      ReflectedChildren(subject: subject, subjectType: subjectType))
Copy the code

ReflectedChildren is an implementation of the subscript() method, which gets the Child

subscript(index: Int) -> Child {
  getChild(of: subject, type: subjectType, index: index)
}
Copy the code

The data that it gets out is

internal func getChild<T> (of value: T.type: Any.Type.index: Int) -> (label: String? , value:Any) {
  var nameC: UnsafePointer<CChar>? = nil
  var freeFunc: NameFreeFunc? = nil
  
  let value = _getChild(of: value, type: type, index: index, outName: &nameC, outFreeFunc: &freeFunc)
  
  let name = nameC.flatMap({ String(validatingUTF8: $0) })
  freeFunc?(nameC)
  return (name, value)
}
Copy the code

As we can see, the _getChild function whose value gets is mapped to the C++ method swift_reflectionMirror_subscript when compiled with the @_silgen_name modifier:

// We intentionally use a non-POD return type with this entry point to give
// it an indirect return ABI for compatibility with Swift.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
// func _getChild<T>(
// of: T,
// type: Any.Type,
// index: Int,
// outName: UnsafeMutablePointer
      
       ? >,
      
// outFreeFunc: UnsafeMutablePointer
      ?>
// ) -> Any
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API
AnyReturn swift_reflectionMirror_subscript(OpaqueValue *value, const Metadata *type,
                                           intptr_t index,
                                           const char **outName,
                                           void (**outFreeFunc)(const char *),
                                           const Metadata *T) {
  return call(value, T, type, [&](ReflectionMirrorImpl *impl) {
    return impl->subscript(index, outName, outFreeFunc);
  });
}
#pragma clang diagnostic pop
Copy the code

The call function ends the call to F with a subclass instance of ReflectionMirrorImpl, and then calls the methods on that instance to do the real work.

Depending on the data type, there are several different sub-class instances of ReflectionMirrorImpl

  • TupleImpl: The data type is Tuple
  • StructImpl: Data type is Struct type
  • EnumImpl: The data type is enumeration (Enum) and Optional.
  • ClassImplObjCClassImpl: The data type is Swift or Objective-Cclass. Because ObjC cannot be guaranteedivarsSo this method could beunsafe.
  • MetatypeImpl: Indicates the data typeMetatypeExistentialMetatype
  • OpaqueImpl: Handles opaque types and returns null.
getFieldAt

Finding elements in structures, classes, and enumerations is pretty complicated right now. The main reason for this complexity is the lack of direct reference relationships between these types and the field descriptors of the fields that contain information about these types.

We use the getFieldAt function to help us find the field descriptor for a given type.

The following code takes a simple intercept and adds a comment:

static std::pair<StringRef /*name*/, FieldType /*fieldInfo*/>
getFieldAt(const Metadata *base, unsigned index) {
  using namespace reflection;
  // If we can't find the field descriptor metadata for that type, return an empty tuple instead.
  auto failedToFindMetadata = [&]() -> std::pair<StringRef, FieldType> {
    ...
    return {"unknown".FieldType(&METADATA_SYM(EMPTY_TUPLE_MANGLING))};
  };
  // Get the type context description of the type
  // Determine whether the obtained context meets the requirements, if not, return an empty tuple
  auto *baseDesc = base->getTypeContextDescriptor(a);if(! baseDesc)return failedToFindMetadata(a);auto *fields = baseDesc->Fields.get(a);if(! fields)return failedToFindMetadata(a);auto &field = fields->getFields()[index];
  // Bounds are always valid as the offset is constant.
  auto name = field.getFieldName(a);// If the field is actually an enumeration, there may be no type.
  if(! field.hasMangledTypeName())
    return {name, FieldType::untypedEnumCase(field.isIndirectCase())};

  auto typeName = field.getMangledTypeName(a);SubstGenericParametersFromMetadata substitutions(base);
  // A reference to a field stores the field type as a symbolic name. Because the callback expects a pointer to the metadata, the symbolic modified name must be converted to a real type.
  auto result = swift_getTypeByMangledName(
      MetadataState::Complete, typeName, substitutions.getGenericArgs(),
      [&substitutions](unsigned depth, unsigned index) {
        return substitutions.getMetadata(depth, index);
      },
      [&substitutions](const Metadata *type, unsigned index) {
        return substitutions.getWitnessTable(type, index);
      });

  // Disassemble the type and pretend it is an empty type instead of a log message
  TypeInfo typeInfo;
  if (result.isError()) {... }else {
    typeInfo = result.getType(a); }// Use the obtained value of the real type
  auto fieldType = FieldType(typeInfo.getMetadata());
  fieldType.setIndirect(field.isIndirectCase());
  fieldType.setReferenceOwnership(typeInfo.getReferenceOwnership());
  fieldType.setIsVar(field.isVar());
  return {name, fieldType};
}
Copy the code
The reflection of the structure

Because some structure types do not fully support reflection, it takes more effort to find names and offsets, and the structure may contain weak references that need to be extracted by reflection code.

The following code takes a simple intercept and adds a comment:

// Implementation for structs.
struct StructImpl : ReflectionMirrorImpl {
  // Check whether the structure supports reflection. The structure metadata stores a field indicating whether it can be reflected
  bool isReflectable(a) {
    const auto *Struct = static_cast<const StructMetadata *>(type);
    const auto &Description = Struct->getDescription(a);return Description->isReflectable(a); }// Struct displayStyle is 's'
  char displayStyle(a) override {
    return 's';
  }
  // The number of child elements is the number of fields given by the metadata, possibly 0 if the type does not actually support reflection
  intptr_t count(a) override {
    if (!isReflectable()) {
      return 0;
    }

    auto *Struct = static_cast<const StructMetadata *>(type);
    return Struct->getDescription()->NumFields;
  }

  intptr_t childOffset(intptr_t i) override {
    auto *Struct = static_cast<const StructMetadata *>(type);

    if (i < 0| | -size_t)i > Struct->getDescription()->NumFields)
      swift::crash("Swift mirror subscript bounds check failure");

    // Load the offset from its respective vector.
    return Struct->getFieldOffsets()[i];
  }
  GetFieldAt: getFieldAt: getFieldAt: getFieldAt
  const FieldType childMetadata(intptr_t i, const char **outName,
                                void (**outFreeFunc)(const char *)) override {
    StringRef name;
    FieldType fieldInfo;
    std::tie(name, fieldInfo) = getFieldAt(type, i);
    assert(! fieldInfo.isIndirect() && "indirect struct fields not implemented");
    
    *outName = name.data(a); *outFreeFunc =nullptr;
    
    return fieldInfo;
  }

  AnyReturn subscript(intptr_t i, const char **outName,
                      void (**outFreeFunc)(const char *)) override {
    auto fieldInfo = childMetadata(i, outName, outFreeFunc);
    // Get the value of the element based on the offset and data
    auto *bytes = reinterpret_cast<char*>(value);
    auto fieldOffset = childOffset(i);
    auto *fieldData = reinterpret_cast<OpaqueValue *>(bytes + fieldOffset);
    // Copy the field value to the return value of type Any to handle weak references
    return copyFieldContents(fieldData, fieldInfo); }};Copy the code

summary

So far, we have basically combed the implementation of structure reflection in Swift. Reflection is implemented by dynamically issuing a subclass instance of ReflectionMirrorImpl.

By reading

If you are looking for Dynamic Features in Swift, check out Raywenderlich’s Dynamic Features in Swift. We may have a discussion of Dynamic Member Lookup in the future.

If you have any questions, comments or feedback, please feel free to contact me. If you wish, you can help more people discover it by sharing this article.

Thank you for reading this! 🚀