In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
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.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.