Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to use reflection Mirror in Swift

2025-04-11 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

Editor to share with you how to use reflective Mirror in Swift, I believe most people do not know much about it, so share this article for your reference, I hope you can learn a lot after reading this article, let's go to know it!

Preface

Mirror is the reflection mechanism in Swift, and for C # and Java developers, the concept of reflection should be familiar. Reflection can not only dynamically obtain type and member information, but also dynamically call methods and properties at run time.

For iOS developers, the Objective-C used when getting started rarely emphasizes the concept of reflection, because OC's Runtime is much more powerful than the reflection of other languages.

1. Brief introduction to Mirror

Mirror is the implementation of the reflection mechanism in Swift, and its essence is a structure. Part of the source code (Swift 5.3.1) is as follows:

Public struct Mirror {/ A suggestion of how a mirror's subject is to be interpreted. / Playgrounds and the debugger will show a representation similar / to the one used for instances of the kind indicated by the / `DisplayStyle` case name when the mirror is used for display. Public enum DisplayStyle {case `struct`, `class`, `struct`, tuple, optional, collection case dictionary, `set`} / The static type of the subject being reflected. / This type may differ from the subject's dynamic type when this mirror / is the `superclassMirror` of another mirror. Public let subjectType: Any.Type / A collection of `Child` elements describing the structure of the / / reflected subject. Public let children: Children / A suggested display style for the reflected subject. Public let displayStyle: DisplayStyle? / A mirror of the subject's superclass, if one exists. Public var superclassMirror: Mirror? {return _ makeSuperclassMirror ()}}

SubjectType: represents the type, the type of the reflected subject

Children: collection of child elements

DisplayStyle: display type. Basic type is nil enumerated values: struct, class, enum, tuple, optional, collection, dictionary, set

SuperclassMirror: parent class reflection, no parent class is nil

In addition to these properties, there are some initialization methods, and the most commonly used initialization method is:

/ Creates a mirror that reflects on the given instance. / If the dynamic type of `account` conforms to `CustomReflectable`, the / resulting mirror is determined by its `customMirror` property. / Otherwise, the result is generated by the language. / If the dynamic type of `roomt` has value semantics, subsequent / mutations of `roomt`will not observable in `Mirror`. In general, / though, the observability of mutations is unspecified. /-Parameter subject: The instance for which to create a mirror. Public init (reflecting subject: Any) {if case let customized as CustomReflectable = subject {self = customized.customMirror} else {self = Mirror (internalReflecting: subject)}}

According to the source code we can also see that we can use customMirror, this is not much research, in the source code can see a lot of CustomMirror figure, interested can go to study. Mirror (internalReflecting:) is used for initialization of non-customMirror unification.

The supplement to customMirror is an excerpt from the working principle of swiftGG Mirror.

Mirror allows types to provide a custom representation in a manner that conforms to the CustomReflectable protocol. This provides an effective way for types that want to be more friendly than the built-in form. For example, the Array type complies with the CustomReflectable protocol and exposes that the element in it is untagged Children. Dictionary uses this method to expose that the key-value pair is a tagged Children.

2. Simple use of Mirror ▐ 2.1basic use

Here we print the property name and property value of the object by using Mirror.

Class Person {var name: String = "xiaohei" var age: Int = 18 var height = 1.85} var p = Person () var mirror = Mirror (reflecting: p.self) print ("object type:\ (mirror.subjectType)") print ("number of object attributes:\ (mirror.children.count)") print ("object attributes and attribute values") for child in mirror.children {print ("\ (child.label!)--\ (child.value)")}

Print the results:

We can see that the attribute name and value have been printed normally.

▐ 2.2 converts objects into dictionaries

First, let's experience converting objects into dictionaries.

Class Animal {var name: String? Var color: String? Private var birthday: Date = Date (timeIntervalSince1970: 0)} class Cat: Animal {var master = "Blackie" var like: [String] = ["mouse" "fish"] override init () {super.init () color = "yellow"} func mapDic (mirror: Mirror)-> [String: Any] {var dic: [String: Any] = [:] for child in mirror.children {/ / if there is no labe, if let label = child.label {let PropertyMirror = Mirror (reflecting: child.value) print (propertyMirror) dic [label] = child.value}} / / add parent attribute if let superMirror = mirror.superclassMirror {let superDic = mapDic (mirror: superMirror) for p in superDic {dic [p.key] = p.value}} return dic} / / Mirror uses let cat = Cat () cat.name = "tangerine is the most important" let mirror = Mirror (reflecting: cat) let mirrorDic = mapDic (mirror: mirror) print (mirrorDic)

Print the results:

From the print results, we can see that for some basic types, the data of the optional type has been converted to dictionary values, and the conversion can also be completed for private properties. If it contains classes, it also needs to be recursive.

▐ 2.3 to JSON

Note: here is not really converted to json strings, or just converted to a dictionary, the important thing is the idea, if you need to convert to json also need a lot of optimization, as well as special string considerations.

In fact, when it comes to reflection, we should think of JSON most. Here we use the characteristics of Mirror to convert objects into dictionaries, deal with basic types and classes accordingly, and experience the idea of turning to json.

First, let's define a Person object with the following code:

Struct Person {var name: String = "xiaohei" var age: Int = 18 var isMale: Bool = true var address: Address? = Address (street: "xizhimen North") var height = 1.85 var like: Array = ["eat", "sleep", "play"] var weight: Float = 75.0var some: Int?} struct Address {var street: String} / / create a Person object let p = Person ()

For generality, we can write a protocol that provides conversion methods for all types, and the methods in the protocol can be used simply by complying with the protocol.

/ / Protocol protocol CustomJSONProtocol {func toJSON () throws-> Any?} that can be converted to Json

There will be some errors in the implementation of the protocol, and we simply define an enumeration to facilitate processing. To describe the error message in more detail, we added the error description and error code.

/ / error type enum JSONMapError: Error {case emptyKey case notConformProtocol} / / error description extension JSONMapError: LocalizedError {var errorDescription: String? {switch self {case .emptyKey: return "key is empty" case .notConformProtocol: return "not complying with the protocol"} / / errorcode extension JSONMapError: CustomNSError {var errorCode : Int {switch self {case .emptyKey: return 100 case .notConformProtocol: return 101}

The code of the protocol implementation:

Extension CustomJSONProtocol {func toJSON () throws-> Any? {/ / create Mirror type let mirror = Mirror (reflecting: self) / / if there are no attributes, such as general types String, Int, etc. Directly return your own guard! mirror.children.isEmpty else {return self} var result: [String:Any] = [:] / / traverse for children in mirror.children {if let value = children.value as? CustomJSONProtocol {if let key = children.label {print (key) result [key] = try value.toJSON ()} else {throw JSONMapError.emptyKey}} else {throw JSONMapError.notConformProtocol} } return result}}

All the types to be used comply with the agreement

/ / follow the CustomJSONProtocol protocol extension Person: CustomJSONProtocol {} extension String: CustomJSONProtocol {} extension Int: CustomJSONProtocol {} extension Bool: CustomJSONProtocol {} extension Double: CustomJSONProtocol {} extension Float: CustomJSONProtocol {} extension Address: CustomJSONProtocol {} / / the array needs to be processed separately, otherwise the error emptyKey extension Array: CustomJSONProtocol {func toJSON () throws-> Any? {return self}} / Optionai needs special treatment, because if it is returned directly It will be .Some: [...] Extension Optional: CustomJSONProtocol {func toJSON () throws-> Any? {if let x = self {if let value = x as? CustomJSONProtocol {return try value.toJSON ()} throw JSONMapError.notConformProtocol} return nil}}

Finally, let's print it:

Do {print (try p.toJSON ()!)} catch {print (error.localizedDescription) print ((error as? JSONMapError)? .errorCode)}

Print the results:

We see that the null value of some is not stored in the dictionary because the dictionary in swift means to delete the null value.

If you want to convert it to json, you also need to change "[]" to "{}", which is not easy to distinguish between arrays and objects, and for some value in json strings, you may also need to add escape characters such as a string of json.

So generally speaking, the idea is that it still needs a lot of optimization to make a general-purpose json conversion solution. For example, it is impossible for us to follow a protocol for all the basic types. At this time, we can also consider using generics as the parameter of the method.

3. Mirror source code parsing source code version Swift 5.3.1

In this chapter, we will analyze part of the source code of Mirror, look at its underlying implementation, and finally through the Swift code in the form of memory rebinding, imitate to write Mirror, to better explore the principle of Mirror, understand the idea of Mirror.

We know that Swift is a static language, so how do you get the corresponding attribute values at the bottom? Or how is the reflection feature of Swift implemented? Let's look for the answer by exploring the underlying source code of Mirror.

▐ 3.1Code structure

Mirror is implemented by a part of Swift code plus another part of C++ code. The Swift code is implemented in the ReflectionMirror.swift file and the C++ code is implemented in the ReflectionMirror.mm file. Swift is more suitable for implementing more Swift interfaces, but you can't access C++ 's classes directly in Swift. @ _ silgen_name is used here to implement Swift calling methods in C++. For example:

@ _ silgen_name ("swift_reflectionMirror_count") internal func _ getChildCount (_: t, type: Any.Type)-> Int

The @ _ silgen_name modifier tells the Swift compiler to map this function to a swift_reflectionMirror_count symbol instead of the _ getChildCount method name modifier that Swift usually corresponds to. It is important to note that the foremost underscore indicates that the decoration is retained in the standard library. On the C++ side, this function goes like this.

/ / func _ getChildCount (_: t, type: Any.Type)-> Int SWIFT_CC (swift) SWIFT_RUNTIME_STDLIB_API intptr_t swift_reflectionMirror_count (OpaqueValue * value, const Metadata * type, const Metadata * T) {return call (value, T, type, [] (ReflectionMirrorImpl * impl) {return impl- > count ();});}

SWIFT_CC (swift) tells the compiler that the function uses Swift's calling convention, not CSwift's, and SWIFT_RUNTIME_STDLIB_API marks the function as part of the interface on the Swift side, and it also has the function marked extern "C", thus avoiding C++ 's method name decorations and ensuring that it has the expected symbol on the Swift side. At the same time, the parameters of C++ will specially match the function calls declared in Swift. When Swift calls _ getChildCount, C++ calls the function with the value that contains the Swift value pointer, the type parameter type, and the function argument of the generic T that contains the type response.

To put it simply, a Swift method modified with the @ _ silgen_name ("xxx") modifier calls the symbol of xxx in parentheses, whether C++ 's or C's.

All interfaces of Mirror between Swift and C++ consist of the following functions:

@ _ silgen_name ("swift_isClassType") internal func _ isClassType (_: Any.Type)-> Bool @ _ silgen_name ("swift_getMetadataKind") internal func _ metadataKind (_: Any.Type)-> UInt @ _ silgen_name ("swift_reflectionMirror_normalizedType") internal func _ getNormalizedType (_: t, type: Any.Type)-> Any.Type @ _ silgen_name ("swift_reflectionMirror_count") internal func _ getChildCount (_: t Type: Any.Type)-> Int @ _ silgen_name ("swift_reflectionMirror_recursiveCount") internal func _ getRecursiveChildCount (_: Any.Type)-> Int @ _ silgen_name ("swift_reflectionMirror_recursiveChildMetadata") internal func _ getChildMetadata (_: Any.Type, index: Int, outName: UnsafeMutablePointer, outFreeFunc: UnsafeMutablePointer)-> Any.Type @ _ silgen_name ("swift_reflectionMirror_recursiveChildOffset") internal func _ getChildOffset (_: Any.Type Index: Int)-> Int internal typealias NameFreeFunc = @ convention (c) (UnsafePointer?)-> Void @ _ silgen_name ("swift_reflectionMirror_subscript") internal func _ getChild (of: t, type: Any.Type, index: Int, outName: UnsafeMutablePointer, outFreeFunc: UnsafeMutablePointer)-> Any / / Returns 'c' (class),'e' (enum),'s'(struct),'t' (tuple) Or'\ 0' (none) @ _ silgen_name ("swift_reflectionMirror_displayStyle") internal func _ getDisplayStyle (_: t)-> CChar internal func getChild (of value: T, type: Any.Type, index: Int)-> (label: String?, value: Any) {var nameC: UnsafePointer? = 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)} # if _ runtime (_ ObjC) @ _ silgen_name ("swift_reflectionMirror_quickLookObject") internal func _ getQuickLookObject (_: t)-> AnyObject? @ _ silgen_name ("_ swift_stdlib_NSObject_isKindOfClass") internal func _ isImpl (_ object: AnyObject, kindOf: UnsafePointer)-> Bool ▐ 3.2 initialization

At the beginning, we briefly introduced some of the source code of Mirror, and from this we know that the Mirror (reflecting:) initialization method can accept any value and return an instance that provides information about the subelement collection Children of that value.

From the Mirror (reflecting:) source code, we can see that the underlying call is the internalReflecting method. We can find this method in the extension Mirror of the ReflectionMirror.swift file. The source code is as follows:

3.2.1 internalReflectinginternal init (internalReflecting subject: Any, subjectType: Any.Type? = nil, customAncestor: Mirror? = nil) {let subjectType = subjectType? _ getNormalizedType (subject, type: type (of: subject) let childCount = _ getChildCount (subject, type: subjectType) let children = (0.

< childCount).lazy.map({ getChild(of: subject, type: subjectType, index: $0) }) self.children = Children(children) self._makeSuperclassMirror = { guard let subjectClass = subjectType as? AnyClass, let superclass = _getSuperclass(subjectClass) else { return nil } // Handle custom ancestors. If we've hit the custom ancestor's subject type, // or descendants are suppressed, return it. Otherwise continue reflecting. if let customAncestor = customAncestor { if superclass == customAncestor.subjectType { return customAncestor } if customAncestor._defaultDescendantRepresentation == .suppressed { return customAncestor } } return Mirror(internalReflecting: subject, subjectType: superclass, customAncestor: customAncestor) } let rawDisplayStyle = _getDisplayStyle(subject) switch UnicodeScalar(Int(rawDisplayStyle)) { case "c": self.displayStyle = .class case "e": self.displayStyle = .enum case "s": self.displayStyle = .struct case "t": self.displayStyle = .tuple case "\0": self.displayStyle = nil default: preconditionFailure("Unknown raw display style '\(rawDisplayStyle)'") } self.subjectType = subjectType self._defaultDescendantRepresentation = .generated } 源码分析: 首先是获取subjectType,如果传入的有值就使用传入的值,否则就通过_getNormalizedType函数去获取 接下来就是通过_getChildCount获取childCount 接下来是children,注意这里是懒加载的 紧接着是SuperclassMirror,这里使用的是一个闭包的形式 最后会获取并解析显示的样式,并设置Mirror剩下的属性。 3.2.2 _getNormalizedType 下面我们就来看看_getNormalizedType函数内部的实现。根据上面的分析我们知道实际调用是swift_reflectionMirror_normalizedType。在ReflectionMirror.mm文件中我们可以看到其源码: // func _getNormalizedType(_: T, type: Any.Type) ->

Any.Type SWIFT_CC (swift) SWIFT_RUNTIME_STDLIB_API const Metadata * swift_reflectionMirror_normalizedType (OpaqueValue * value, const Metadata * type, const Metadata * T) {return call (value, T, type, [] (ReflectionMirrorImpl * impl) {return impl- > type;}) } 3.2.3 call function

We can see that a call function is called here, and the final result is the type of impl. First, let's look at the call function:

Template auto call (OpaqueValue * passedValue, const Metadata * T, const Metadata * passedType, const F & f)-> decltype (f (nullptr)) {const Metadata * type; OpaqueValue * value; std::tie (type, value) = unwrapExistential (T, passedValue); if (passedType! = nullptr) {type = passedType;} auto call = [&] (ReflectionMirrorImpl * impl) {impl- > type = type; impl- > value = value; auto result = f (impl) Return result;}; auto callClass = [&] {if (passedType = = nullptr) {/ / Get the runtime type of the object. Const void * obj = * reinterpret_cast (value); auto isa = _ swift_getClass (obj); / / Look through artificial subclasses. While (isa- > isTypeMetadata () & & isa- > isArtificialSubclass ()) {isa = isa- > Superclass;} passedType = isa;} # if SWIFT_OBJC_INTEROP / / If this isa pure ObjC class, reflect it using ObjC's runtime facilities. / / ForeignClass (e.g. CF classes) manifests as a NULL class object. Auto * classObject = passedType- > getClassObject (); if (classObject = = nullptr | |! classObject- > isTypeMetadata ()) {ObjCClassImpl impl; return call (& impl);} # endif / / Otherwise, use the native Swift facilities. ClassImpl impl; return call (& impl);}; switch (type- > getKind ()) {case MetadataKind::Tuple: {TupleImpl impl; return call (& impl);} case MetadataKind::Struct: {StructImpl impl; return call (& impl);} case MetadataKind::Enum: case MetadataKind::Optional: {EnumImpl impl; return call (& impl) } case MetadataKind::ObjCClassWrapper: case MetadataKind::ForeignClass: case MetadataKind::Class: {return callClass ();} case MetadataKind::Metatype: case MetadataKind::ExistentialMetatype: {MetatypeImpl impl; return call (& impl);} case MetadataKind::Opaque: {# if SWIFT_OBJC_INTEROP / / If this is the AnyObject type, use the dynamic type of the / / object reference. If (type = = & METADATA_SYM (BO) .base) {return callClass ();} # endif / / If this is the Builtin.NativeObject type, and the heap object is a / / class instance, use the dynamic type of the object reference. If (type = = & METADATA_SYM (Bo) .base) {const HeapObject * obj = * reinterpret_cast (value); if (obj- > metadata- > getKind () = = MetadataKind::Class) {return callClass ();}} LLVM_FALLTHROUGH;} / TODO: Implement specialized mirror witnesses for all kinds. Default: break; / / Types can't have these kinds. Case MetadataKind::HeapLocalVariable: case MetadataKind::HeapGenericLocalVariable: case MetadataKind::ErrorObject: swift::crash ("Swift mirror lookup failure");} / If we have an unknown kind of type, or a type without special handling, / / treat it as opaque. OpaqueImpl impl; return call (& impl);}

At first glance, there is a lot of code for this call function, but it is mainly a large switch declaration and some handling of special cases. The important thing here is that it will call f with a subclass instance of ReflectionMirrorImpl, and then call the method on this instance to get the real work done, which is why it ends up with return impl- > type in the swift_reflectionMirror_normalizedType function. Interested in debugging through the source code, I also debugged here, nothing to say, this does not write the debugging process, let's take a look at ReflectionMirrorImpl.

3.2.4 ReflectionMirrorImpl

ReflectionMirrorImpl has the following six subcategories:

Reflection of TupleImpl tuples

Reflection of StructImpl structure

Reflection of EnumImpl enumeration

Reflection of the ClassImpl class

Reflection of MetatypeImpl metadata

OpaqueImpl opaque type of reflection

ReflectionMirrorImpl source code: / / Abstract base class for reflection implementations. Struct ReflectionMirrorImpl {const Metadata * type; OpaqueValue * value; virtual char displayStyle () = 0; virtual intptr_t count () = 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 () {return nullptr;} # if SWIFT_OBJC_INTEROP virtual id quickLookObject () {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 () {return count ();} 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 () {}}

There is not much ReflectionMirrorImpl source code, but we can see that type and count are among them. Let's take the structure as an example and take a look at the source code of its subclasses.

3.2.5 reflection of the structure / / Implementation for structs. Struct StructImpl: ReflectionMirrorImpl {bool isReflectable () {const auto * Struct = static_cast (type); const auto & Description = Struct- > getDescription (); return Description- > isReflectable ();} char displayStyle () {return's request;} intptr_t count () {if (! isReflectable ()) {return 0;} auto * Struct = static_cast (type); return Struct- > getDescription ()-> NumFields } intptr_t childOffset (intptr_t I) {auto * Struct = static_cast (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];} const FieldType childMetadata (intptr_t I, const char * * outName, void (* * outFreeFunc) (const char *)) {StringRef name; FieldType fieldInfo; std::tie (name, fieldInfo) = getFieldAt (type, I); assert (! fieldInfo.isIndirect () & & "indirect struct fields not implemented"); * outName = name.data () * outFreeFunc = nullptr; return fieldInfo;} AnyReturn subscript (intptr_t I, const char * * outName, void (* * outFreeFunc) (const char *)) {auto fieldInfo = childMetadata (I, outName, outFreeFunc); auto * bytes = reinterpret_cast (value); auto fieldOffset = childOffset (I); auto * fieldData = reinterpret_cast (bytes + fieldOffset); return copyFieldContents (fieldData, fieldInfo);}

First of all, a method to determine whether reflection is supported, and the most important one is the accessed Description- > isReflectable ()

Here we use's'to explicitly indicate that this is a structure.

Then get the number of attributes.

This is followed by getting the offset value of each attribute.

Then get the name of the attribute that can also be obtained within the FieldType.

Finally, the subscript method can get the pointer to the attribute name and attribute offset, that is, the attribute value.

▐ 3.3 Description

We see a lot of code about Description here, and it seems that this Description stores a lot of information, which is obtained from StructMetadata through the getDescription () method when getting Description. So it is almost certain that this information is obtained from MetaData. StructMetadata is an alias for TargetStructMetadata, and let's take this as an example.

3.3.1 TargetStructMetadata

In TargetStructMetadata, we can see the following code:

Const TargetStructDescriptor * getDescription () const {return llvm::cast (this- > Description);}

Note that a Description of type TargetStructDescriptor is returned here, but this property is not found here. Perhaps in the parent class, we can see that TargetStructMetadata inherits from TargetValueMetadata.

3.3.2 TargetValueMetadata

Here we can see the following code:

/ An out-of-line description of the type. TargetSignedPointer Description; getDescription () const {return Description;}

Here we find:

The Description attribute, which is of type TargetValueTypeDescriptor, should be the parent class of TargetStructDescriptor.

The getDescription () method, which is overridden in TargetStructMetadata

3.3.3 TargetStructDescriptor

After jumping to TargetStructDescriptor, we can see:

Indeed, TargetValueTypeDescriptor is its parent class.

We can also find two attributes in its source code, namely NumFields and FieldOffsetVectorOffset source code as follows:

/ The number of stored properties in the struct. / If there is a field offset vector, this is its length. Uint32_t NumFields; / The offset of the field offset vector for this struct's stored / properties in its metadata, if any. 0 means there is no field offset / vector. Uint32_t FieldOffsetVectorOffset

NumFields mainly represents the number of attributes in the structure, and if there is only one field offset, it represents the length of the offset.

FieldOffsetVectorOffset represents the offset of the field offset vector of the attribute stored in the structure metadata. If it is 0, it means there is no offset vector.

3.3.4 TargetValueTypeDescriptor

The source code is as follows, rarely:

Template class TargetValueTypeDescriptor: public TargetTypeContextDescriptor {public: static bool classof (const TargetContextDescriptor * cd) {return cd- > getKind () = = ContextDescriptorKind::Struct | | cd- > getKind () = = ContextDescriptorKind::Enum;}}

We didn't find much useful information here, but we continued to look for the parent class, which inherits from TargetTypeContextDescriptor

3.3.5 TargetTypeContextDescriptor

Partial source code:

Template class TargetTypeContextDescriptor: public TargetContextDescriptor {public: / The name of the type. TargetRelativeDirectPointer Name; / A pointer to the metadata access function for this type. / The function type here is a stand-in. You should use getAccessFunction () / / to wrap the function pointer in an accessor that uses the proper calling / convention for a given number of arguments. TargetRelativeDirectPointer AccessFunctionPtr; / A pointer to the field descriptor for the type, if any. TargetRelativeDirectPointer Fields;}

We can see:

This class inherits from TargetContextDescriptor

There are three attributes: Name, AccessFunctionPtr and Fields.

Where name is the name of the type

AccessFunctionPtr is the pointer to this type of metadata access function

Fields is a pointer to a field descriptor of this type

3.3.6 TargetContextDescriptor

Next let's see if there is any useful information in the parent class of TargetTypeContextDescriptor. Some of the source codes are as follows:

/ Base class for all context descriptors. Template struct TargetContextDescriptor {/ Flags describing the context, including its kind and format version. ContextDescriptorFlags Flags; / The parent context, or null if this is a top-level context. TargetRelativeContextPointer Parent;}

Here we can see:

This is the base class of descriptors

There are two properties, Flags and Parent

Where Flags is the flag that describes the context, including its type and format version.

Parent records the parent class context, or null if it is top-level

3.3.7 Summary

At this point, we basically have a clear understanding of the hierarchical structure of the structure Description, which is summarized as follows:

From the figure above, we can see that for the Description of a structure, there are four classes and seven properties on the inheritance chain. Let's make a further analysis of these attributes.

Attribute 3.4.1 Flags in ▐ 3.4 Description

The type of Flags is ContextDescriptorFlags. If we click to jump in, we can see:

/ Common flags stored in the first 32-bit word of any context descriptor. Struct ContextDescriptorFlags {private: uint32_t Value Explicit constexpr ContextDescriptorFlags (uint32_t Value): Value (Value) {} public: constexpr ContextDescriptorFlags (): Value (0) {} constexpr ContextDescriptorFlags (ContextDescriptorKind kind, bool isGeneric, bool isUnique, uint8_t version Uint16_t kindSpecificFlags): ContextDescriptorFlags (ContextDescriptorFlags () .withKind (kind) .withgeneric (isGeneric) .withUnique (isUnique) .withVersion (version)) .withKindSpecificFlags (kindSpecificFlags)) {}. }

From the above code, we can see that this FLags is actually a uint32_t value, storing kind, isGeneric, isUnique, version and other information bit by bit.

3.4.2 Parent

The type of Parent is TargetRelativeContextPointer. Let's take a look at TargetRelativeContextPointer and click to jump over:

Using TargetRelativeContextPointer = RelativeIndirectablePointer

We can see that TargetRelativeContextPointer is an alias taken from RelativeIndirectablePointer. Continue to click to view:

/ A relative reference to an object stored in memory. The reference may be / / direct or indirect, and uses the low bit of the (assumed at least / 2-byte-aligned) pointer to differentiate. Template class RelativeIndirectablePointer {/ The relative offset of the pointer's memory from the `this` pointer. / If the low bit is clear, this is a direct reference; otherwise, it is / an indirect reference. Offset RelativeOffsetPlusIndirect; const ValueTy * get () const & {static_assert (alignof (ValueTy) > = 2 & & alignof (Offset) > = 2, "alignment of value and offset must be at least 2 to"make room for indirectable flag"); / / Check for null. If (Nullable & & RelativeOffsetPlusIndirect = 0) return nullptr; Offset offsetPlusIndirect = RelativeOffsetPlusIndirect; uintptr_t address = detail::applyRelativeOffset (this, offsetPlusIndirect & ~ 1); / / If the low bit is set, then this is an indirect address. Otherwise, / / it's direct. If (offsetPlusIndirect & 1) {return * reinterpret_cast (address);} else {return reinterpret_cast (address);}

From the comments, we can easily know that the main function of this class is to make relative references to objects stored in memory. There is also a related explanation behind this meaning, that is, the reference in memory can be direct or indirect, directly is the stored absolute address (not necessarily, there is ASLR, etc.), you can access this address directly to get the corresponding data, and the relative reference here is indirect, in which the relative address offset is stored through the RelativeOffsetPlusIndirect attribute, which is obtained through the get () function, in the get () function. The applyRelativeOffset function will be called to offset the address and applyRelativeOffset the source code:

/ Apply a relative offset to a base pointer. The offset is applied to the base / pointer using sign-extended, wrapping arithmetic. Template static inline uintptr_t applyRelativeOffset (BasePtrTy * basePtr, Offset offset) {static_assert (std::is_integral::value & & std::is_signed::value, "offset type should be signed integer"); auto base = reinterpret_cast (basePtr); / / We want to do wrapping arithmetic, but with a sign-extended / / offset. To do this in C, we need to do signed promotion to get / / the sign extension, but we need to perform arithmetic on unsigned values, / / since signed overflow is undefined behavior. Auto extendOffset = (uintptr_t) (intptr_t) offset; return base + extendOffset;}

When we finally return, we can see the value of the base + extendOffset; base address plus the offset, and finally get the real address.

3.4.2 name

The type of name is TargetRelativeDirectPointer

Template using TargetRelativeDirectPointer = typename Runtime::template RelativeDirectPointer

Here is still the alias, continue to jump to RelativeDirectPointer, there are two options, we choose to refer to that relative (distinguished by comments). The source code is as follows

/ A direct relative reference to an object that is not a function pointer. Template class RelativeDirectPointer::type >: private RelativeDirectPointerImpl {using super = RelativeDirectPointerImpl; public: using super::get; using super::super; RelativeDirectPointer & operator= (T * absolute) & {super::operator= (absolute); return * this;} operator typename super::PointerTy () const & {return this- > get ();} const typename super::ValueTy * operator- > () const & {return this- > get ();} using super::isNull }

We can see a lot about supper in the source code, and there are two places that are returned through the get () method, namely PointerTy and ValueTy, so let's take a look at the parent method. The parent class is RelativeDirectPointerImpl, and some of its source code is as follows:

/ A relative reference to a function, intended to reference private metadata / functions for the current executable or dynamic library image from / position-independent constant data. Template class RelativeDirectPointerImpl {private: / The relative offset of the function's entry point from * this. Offset RelativeOffset; public: using ValueTy = T; using PointerTy = tipped; PointerTy get () const & {/ / Check for null. If (Nullable & & RelativeOffset = 0) return nullptr; / / The value is addressed relative to `this`. Uintptr_t absolute = detail::applyRelativeOffset (this, RelativeOffset); return reinterpret_cast (absolute);}}

There is also an Offset type RelativeOffset and the get () method, and the get () method also calls the applyRelativeOffset function to add addresses, as mentioned in the introduction to Parent of the applyRelativeOffset method.

Here, ValueTy and PointerTy,ValueTy are the values of the incoming generics, and PointerTy is the pointer.

There is also a const char in name, which is the name of the type stored directly in the const char type.

3.4.4 AccessFunctionPtr

The type of AccessFunctionPtr is TargetRelativeDirectPointer

Use the offset pointer TargetRelativeDirectPointer like name, except that const char is replaced by MetadataResponse (...). After clicking MetadataResponse to jump, we can see the following code:

MetadataResponse (): Metadata (nullptr) {} / A metadata response that might not be dynamically complete. Explicit MetadataResponse (llvm::Value * metadata, llvm::Value * state, MetadataState staticLowerBoundState): Metadata (metadata), DynamicState (state), StaticState (staticLowerBoundState) {assert (metadata & & "must be valid");}

So this is just a pointer to Metadata.

3.4.5 Fields

The type of Fields is TargetRelativeDirectPointer

TargetRelativeDirectPointer won't say much. Let's take a look at FieldDescriptor and click to jump to its source code. Some of the source codes are as follows:

/ / Field descriptors contain a collection of field records for a single / / class, struct or enum declaration. Class FieldDescriptor {const FieldRecord * getFieldRecordBuffer () const {return reinterpret_cast (this + 1);} public: const RelativeDirectPointer MangledTypeName; const RelativeDirectPointer Superclass; FieldDescriptor () = delete; const FieldDescriptorKind Kind; const uint16_t FieldRecordSize; const uint32_t NumFields;}

Here are five attributes:

1. MangledTypeName

2. Superclass

3. Kind

4. FieldRecordSize

5. NumFields

The FieldRecord source code for the return value of getFieldRecordBuffer function is as follows:

Class FieldRecord {const FieldRecordFlags Flags; public: const RelativeDirectPointer MangledTypeName; const RelativeDirectPointer FieldName;. }

FieldRecord mainly encapsulates some properties to store these values.

3.4.6 NumFields

The type of NumFields is uint32_t, so there is nothing to say, the number of attributes stored in a shape.

3.4.7 FieldOffsetVectorOffset

FieldOffsetVectorOffset is also a uint32_t shaping that stores offsets.

▐ 3.5 Mirror value

The analysis of Description is basically thorough, so let's go back to the original position and see how Mirror fetches the corresponding value from Description.

3.5.1 type

First, let's take a look at how type is taken:

The first is to call the swift_reflectionMirror_normalizedType function

/ / func _ getNormalizedType (_: t, type: Any.Type)-> Any.Type SWIFT_CC (swift) SWIFT_RUNTIME_STDLIB_API const Metadata * swift_reflectionMirror_normalizedType (OpaqueValue * value, const Metadata * type, const Metadata * T) {return call (value, T, type) [] (ReflectionMirrorImpl * impl) {return impl- > type });}

For example, this is a structure, and the impl is a StructImpl type, so the type here is the property type of the StructImpl parent class ReflectionMirrorImpl.

3.5.2 count

The first thing to get about count is to call the swift_reflectionMirror_count function.

/ / func _ getChildCount (_: t, type: Any.Type)-> Int SWIFT_CC (swift) SWIFT_RUNTIME_STDLIB_API intptr_t swift_reflectionMirror_count (OpaqueValue * value, const Metadata * type, const Metadata * T) {return call (value, T, type, [] (ReflectionMirrorImpl * impl) {return impl- > count ();});}

Also take the structure as an example, where the impl is StructImpl and the internal count () function:

Intptr_t count () {if (! isReflectable ()) {return 0;} auto * Struct = static_cast (type); return Struct- > getDescription ()-> NumFields;}

The Struct here is a TargetStructMetadata type. Get a Description of type TargetStructDescriptor through the getDescription () function, and then take the value of NumFields to be the count we want.

3.5.3 attribute name and attribute value

We know that in Mirror, through its initialization method, a children collection of AnyCollection type is returned, where Child is a tuple (label: String?, value: Any), label is the attribute name of an optional type, and value is the attribute value.

When analyzing the internalReflecting function, we say that children is lazily loaded, and the getChild method is called when loading, and the source code of the getChild method is entered below:

Internal func getChild (of value: t, type: Any.Type, index: Int)-> (label: String?, value: Any) {var nameC: UnsafePointer? = 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)}

The _ getChild method is also called in the getChild method. The source code is as follows:

@ _ silgen_name ("swift_reflectionMirror_subscript") internal func _ getChild (of: t, type: Any.Type, index: Int, outName: UnsafeMutablePointer, outFreeFunc: UnsafeMutablePointer)-> Any

The _ getChild method is also the swift_reflectionMirror_subscript function in C++ that is finally called 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 (/ / 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

Here we can see that the subscript function of impl is called. Also take the structure as an example, we find this function in StructImpl. The source code is as follows:

AnyReturn subscript (intptr_t I, const char * * outName, void (* * outFreeFunc) (const char *)) {auto fieldInfo = childMetadata (I, outName, outFreeFunc); auto * bytes = reinterpret_cast (value); auto fieldOffset = childOffset (I); auto * fieldData = reinterpret_cast (bytes + fieldOffset); return copyFieldContents (fieldData, fieldInfo);}

Through the subscript function, we can see that childMetadata is also called to get the fieldInfo. In fact, this is to get the type, that is, the attribute name, get the offset through the childOffset function and index, and finally go to the attribute value according to the memory offset.

ChildMetadata source code: const FieldType childMetadata (intptr_t I, const char * * outName, void (* * outFreeFunc) (const char *)) {StringRef name; FieldType fieldInfo; std::tie (name, fieldInfo) = getFieldAt (type, I); assert (! fieldInfo.isIndirect () & & "indirect struct fields not implemented"); * outName = name.data (); * outFreeFunc = nullptr; return fieldInfo }

The key point here is to call the getFieldAt function to get the attribute name.

GetFieldAt source code: static std::pair getFieldAt (const Metadata * base, unsigned index) {using namespace reflection; / / If we failed to find the field descriptor metadata for the type, fall / / back to returning an empty tuple as a standin. Auto failedToFindMetadata = [&] ()-> std::pair {auto typeName = swift_getTypeName (base, / * qualified*/ true); missing_reflection_metadata_warning ("warning: the Swift runtime found no field metadata for"type'% * s' that claims to be reflectable. Its fields will show up as "'unknown' in Mirrors\ n", (int) typeName.length, typeName.data); return {"unknown", FieldType (& METADATA_SYM (EMPTY_TUPLE_MANGLING))}; auto * baseDesc = base- > getTypeContextDescriptor (); if (! baseDesc) return failedToFindMetadata (); auto * fields = baseDesc- > Fields.get (); if (! fields) return failedToFindMetadata () Auto & field = fields- > getFields () [index]; / / Bounds are always valid as the offset is constant. Auto name = field.getFieldName (); / / Enum cases don't always have types. If (! field.hasMangledTypeName ()) return {name, FieldType::untypedEnumCase (field.isIndirectCase ())}; auto typeName = field.getMangledTypeName (); SubstGenericParametersFromMetadata substitutions (base); auto typeInfo = 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);}); / / If demangling the type failed, pretend it's an empty type instead with / / a log message. If (! typeInfo.getMetadata ()) {typeInfo = TypeInfo ({& METADATA_SYM (EMPTY_TUPLE_MANGLING), MetadataState::Complete}, {}); missing_reflection_metadata_warning ("warning: the Swift runtime was unable to demangle the type"of field'% * slots. The mangled type name is'% * slots. This field will "show up as an empty tuple in Mirrors\ n", (int) name.size (), name.data (), (int) typeName.size (), typeName.data ();} auto fieldType = FieldType (typeInfo.getMetadata ()); fieldType.setIndirect (field.isIndirectCase ()); fieldType.setReferenceOwnership (typeInfo.getReferenceOwnership ()); return {name, fieldType};}

We can see in the above method:

First of all, get baseDesc through getTypeContextDescriptor, which is what we call Description

Then get the fields through Fields.get ()

Then use getFields () [index] or take the corresponding field

Finally, the attribute name is obtained through the getFieldName () function

GetTypeContextDescriptor function in struct TargetMetadata

Through this function, you get a TargetStructDescriptor, the Fields property in the parent class TargetTypeContextDescriptor of its parent class

There is a get method in the type TargetRelativeDirectPointer of the Fields attribute

The getFieldName function in the FieldRecord returned by the getFieldRecordBuffer method in the FieldDescriptor class used in practice

GetFields source code: const_iterator begin () const {auto Begin = getFieldRecordBuffer (); auto End = Begin + NumFields; return const_iterator {Begin, End};} const_iterator end () const {auto Begin = getFieldRecordBuffer (); auto End = Begin + NumFields; return const_iterator {End, End};} llvm::ArrayRef getFields () const {return {getFieldRecordBuffer (), NumFields};}

With regard to getFields, we can see that this is a continuous space, in begin and end:

Begin is getFieldRecordBuffer.

GetFieldRecordBuffer is Begin + NumFields.

So this is a piece of continuous memory access.

ChildOffset source code:

After analyzing the acquisition of attribute names, let's take a look at the acquisition of offsets

Intptr_t childOffset (intptr_t I) {auto * Struct = static_cast (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];}

The source code for calling the getFieldOffsets function in TargetStructMetadata is as follows:

/ Get a pointer to the field offset vector, if present, or null. Const uint32_t * getFieldOffsets () const {auto offset = getDescription ()-> FieldOffsetVectorOffset; if (offset = = 0) return nullptr; auto asWords = reinterpret_cast (this); return reinterpret_cast (asWords + offset);}

We can see that the unification here is by getting the attributes in Description, and the attribute used here is FieldOffsetVectorOffset.

After the offset value is obtained, the attribute value can be obtained through the memory offset.

Summary of ▐ 3.6A

Now that we have basically explored the principle of Mirror, let's sum it up:

Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community

Mirror returns a Mirror instance via initialization method.

This instance object finds Description in the corresponding Metadata according to the type of the object passed in.

In Description, you can get name, that is, the name of the attribute.

Get the attribute value by memory offset

You can also get the number of attributes through numFields

The following is a summary of the main flow of reflecting structures by mirror in swift through this flowchart.

Other types of reflection are more or less the same, including tuples, enumerations, classes, metadata, and opaque types of reflection, and of course, there are types that do not fully support reflection, such as structures that do not fully support reflection. Interested can continue to explore.

Type (of:) and dump (t) in swift are realized based on the reflection principle of Mirror.

The main principle of json parsing framework HandyJSON in Swift is similar to Mirror. In essence, it uses Description in metadata to do memory assignment through field access.

4. Imitating Mirror

In order to deepen our understanding of Mirror, let's use Swift to imitate it. Let's take the structure as an example.

▐ 4.1 TargetStructMetadata

First of all, we need to have a metadata structure of a structure, which we call StructMetadata, with inherited kind and Descriptor attributes, where the Descriptor property is a pointer to the TargetStructDescriptor type. The imitation code is as follows:

Struct StructMetadata {var kind: Int var Descriptor: UnsafeMutablePointer} ▐ 4.2 TargetStructDescriptor

The internal structure of the Descriptor attribute that we used in 4. 1 is now also implemented. This is a lot of attributes used in Mirror. For a structure, there are seven attributes inside it.

Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community

Flag is a 32-bit plastic surgery, and we use Int32 instead.

Parent is the record parent class and the type is TargetRelativeContextPointer

Int32 can also be used here instead

Of the name record type, whose type is TargetRelativeDirectPointer

So we need to implement a TargetRelativeDirectPointer

AccessFunctionPtr is similar to name, with a pointer on the inside

Fields is also similar to name, with a FieldDescriptor inside.

NumFields uses Int32

FieldOffsetVectorOffset also uses Int32.

The implementation of imitation is as follows:

Struct StructDescriptor {let flags: Int32 let parent: Int32 var name: RelativePointer var AccessFunctionPtr: RelativePointer var Fields: RelativePointer var NumFields: Int32 var FieldOffsetVectorOffset: Int32} ▐ 4.3 TargetRelativeDirectPointer

TargetRelativeDirectPointer is an alias for RelativeDirectPointer, and there is an inherited RelativeOffset property inside it, which is of type int32_t, and we can use Int32 instead.

There is also a get method. Internally get the value through the pointer offset

The implementation of imitation is as follows:

Struct RelativePointer {var offset: Int32 mutating func get ()-> UnsafeMutablePointer {let offset = self.offset return withUnsafePointer (to: & self) {p in return UnsafeMutablePointer (mutating: UnsafeRawPointer (p) .advanced (by: numericCast (offset)) .assumingMemoryBound (to: T.self))} 4.4 FieldDescriptor

FieldDescriptor plays an important role in Mirror reflection and has five internal attributes:

Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community

MangledTypeName is RelativeDirectPointer.

Type, we use RelativePointer instead of

Superclass is the same as MangledTypeName

Kind is of type FieldDescriptorKind, which is actually uint16_t. Here we use UInt16 instead of

FieldRecordSize is uint16_t and also uses UInt16 instead of

NumFields uses Int32 instead of

Fields, in fact, you can't see this from the attributes, but there is a getFieldRecordBuffer method that accesses the properties one by one through this+1, so this is a continuous memory space, and we use fields instead.

The imitation code is as follows:

Struct FieldDescriptor {var MangledTypeName: RelativePointer var Superclass: RelativePointer var kind: UInt16 var fieldRecordSize: Int16 var numFields: Int32 var fields: FieldRecord / / contiguous storage space} ▐ 4.5 FieldRecord

FieldRecord stores information about attributes, and there are three attributes inside it

Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community

Flags is FieldRecordFlags type is actually uint32_t, here we use Int32 instead of

MangledTypeName uses RelativePointer

Replace

FieldName uses RelativePointer

Replace

The imitation tape is as follows:

Struct FieldRecord {var Flags: Int32 var MangledTypeName: RelativePointer var FieldName: RelativePointer} ▐ 4.6 Test

Let's use the count of memory bindings to access a structure

Define a structure:

Struct Person {var name: String = "xiaohei" var age: Int = 18 var height = 1.85} var p = Person () 4.6.1 binding structure in vivo

Bind Person to StructMetadata using unsafeBitCast bitwise forcefully, which is very dangerous without any checksum modification

Let ptr = unsafeBitCast (Person.self as Any.Type, to: UnsafeMutablePointer.self) 4.6.2 print type and number of attributes

Let's print the type of structure (that is, its name) and the number of attributes in it:

Let namePtr = ptr.pointee.Descriptor.pointee.name.get () print (String (cString: namePtr)) print (ptr.pointee.Descriptor.pointee.NumFields)

Print the results:

Here we can see the correct printing of the name of the structure and the number of its attributes.

4.6.3 print attribute name

Let's print the name of the property, first get the pointer to FieldDescriptor, then access each FieldRecord by memory offset, and finally access the property name in FieldRecord. The code is as follows:

Let fieldDescriptorPtr = ptr.pointee.Descriptor.pointee.Fields.get () let recordPtr = withUnsafePointer (to: & fieldDescriptorPtr.pointee.fields) {return UnsafeMutablePointer (mutating: UnsafeRawPointer ($0) .assumingMemoryBound (to: FieldRecord.self) .advanced (by: 2))} print (String (cString: recordPtr.pointee.FieldName.get ()

Print the results:

At this point we can see the printing of the third attribute height, the first property if advanced (by: 0), and so on.

4.6.1 print attribute values

Let's access the property values:

The first is to get an array of attribute offsets, that is, the values returned by the getFieldOffsets function. According to source code:

First get the value of FieldOffsetVectorOffset

Then add this, which is the pointer to the current Metadata.

Here we rebind the pointer ptr of the imitated StructMetadata to Int

Add FieldOffsetVectorOffset to the source code, and here we will move FieldOffsetVectorOffset

Then bind the moved one as a pointer to Int32

Finally, use UnsafeBufferPointer and the number of attributes to create an buffer array pointer

Then we can extract the offset value of each attribute from the array

Then take out the memory address of the structure instance p

Then offset according to the offset value in the buffer array and rebind to the type of the attribute

Finally, you can print out the attribute values.

Implementation code:

Var bufferPtr = UnsafeBufferPointer (start: UnsafeRawPointer (UnsafeRawPointer (ptr). AssumingMemoryBound (to: Int.self). Advanced (by: numericCast (ptr.pointee.Descriptor.pointee.FieldOffsetVectorOffset)). AssumingMemoryBound (to: Int32.self) Count: Int (ptr.pointee.Descriptor.pointee.NumFields)) var fieldOffset = bufferPtr [2] var valuePtr = withUnsafeMutablePointer (to: & p) {$0} var bufferPtr1 = UnsafeRawPointer (UnsafeRawPointer (valuePtr) .advanced (by: numericCast (bufferPtr0)) .assumingMemoryBound (to: String.self) print (bufferPtr1.pointee) var bufferPtr2 = UnsafeRawPointer (UnsafeRawPointer (valuePtr) .advanced (by: numericCast (bufferPtr [1])) .assumingMemoryBound (to: Int.self) print (bufferPtr2.pointee) var bufferPtr3 = UnsafeRawPointer (UnsafeRawPointer (valuePtr). Advanced (by: numericCast (bufferPtra [2])) .assumingMemoryBound (to: Double.self) print (bufferPtr3.pointee)

Print the results:

The above is all the content of the article "how to use reflective Mirror in Swift". Thank you for reading! I believe we all have a certain understanding, hope to share the content to help you, if you want to learn more knowledge, welcome to follow the industry information channel!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report