Swift 类似HandyJSON解析Struct
Swift 类似HandyJSON解析Struct
- HandyJSON
- 从源码解析Struct
- 获取TargetStructMetadata
- 获取TargetStructDescriptor
- 实现TargetRelativeDirectPointer
- FieldDescriptor和FieldRecord
- fieldOffsetVectorOffset计算偏移量
- 代码的验证
HandyJSON
HandyJSON
是阿里开发的一个在swift
上把JSON
数据转化为对应model
的框架。与其他流行的Swift JSON
库相比,HandyJSON
的特点是,它支持纯swift
类,使用也简单。它反序列化时(把JSON
转换为Model
)不要求Model
从NSObject
继承(因为它不是基于KVC
机制),也不要求你为Model
定义一个Mapping
函数。只要你定义好Model
类,声明它服从HandyJSON
协议,HandyJSON
就能自行以各个属性的属性名为Key
,从JSON
串中解析值。不过因为HandyJSON
是基于swift
的metadata
来做的,如果swift
的metadata
的结构改了,HandyJSON
可能就直接不能用了。当然阿里一直在维护这个框架,swift
的源码有变化,相信框架也是相对于有改变的。
HandyJSON的github
从源码解析Struct
获取TargetStructMetadata
由于HandyJSON
是基于swift
的metadata
来做的,说道解析解析struct
,那就不得不去了解metadata
。接下来,我们会从源码的角度去寻找metadata
。
首先,我们从源码Metadata.h
中搜索StructMetadata
相关信息,会发现其真正类型是TargetStructMetadata
。
using StructMetadata = TargetStructMetadata<InProcess>;
接着,我们查看TargetStructMetadata
的结构会发现,TargetStructMetadata
继承自TargetValueMetadata
,TargetValueMetadata
继承自TargetMetadata
。
struct TargetStructMetadata : public TargetValueMetadata<Runtime> {struct TargetValueMetadata : public TargetMetadata<Runtime> {
那么,我们就可以通过这个继承链去还原TargetStructMetadata
的结构。
从代码中我们可以看出,TargetStructMetadata
的第一个属性是Kind
,除了这个属性还有一个description
,用于记录描述文件。
struct TargetMetadata {......private:/// The kind. Only valid for non-class metadata; getKind() must be used to get/// the kind value.StoredPointer Kind;......
}struct TargetValueMetadata : public TargetMetadata<Runtime> {using StoredPointer = typename Runtime::StoredPointer;TargetValueMetadata(MetadataKind Kind,const TargetTypeContextDescriptor<Runtime> *description): TargetMetadata<Runtime>(Kind), Description(description) {}//用于记录元数据的描述/// An out-of-line description of the type.TargetSignedPointer<Runtime, const TargetValueTypeDescriptor<Runtime> * __ptrauth_swift_type_descriptor> Description;......
}
这样我们就可以得到TargetStructMetadata
的结构为
struct TargetStructMetadata {// StoredPointer Kind; 64位系统下 using StoredPointer = uint64_t; 即为Intvar kind: Int //暂且先定义为UnsafeMutablePointer,后面会分析typeDescriptor的结构 T就是泛型var typeDescriptor: UnsafeMutablePointer<T>
}
获取TargetStructDescriptor
接下来我们解析Description
的相关信息。从源码中可得TargetStructDescriptor
是Description
的结构。
const TargetStructDescriptor<Runtime> *getDescription() const {return llvm::cast<TargetStructDescriptor<Runtime>>(this->Description);}
我们查找TargetStructDescriptor
可以得到,其继承自TargetValueTypeDescriptor
,含有两个属性NumFields
(记录属性的count
)和FieldOffsetVectorOffset
(记录属性在metadata
中的偏移量)
class TargetStructDescriptor final: public TargetValueTypeDescriptor<Runtime>,public TrailingGenericContextObjects<TargetStructDescriptor<Runtime>,TargetTypeGenericContextDescriptorHeader,/*additional trailing objects*/TargetForeignMetadataInitialization<Runtime>,TargetSingletonMetadataInitialization<Runtime>,TargetCanonicalSpecializedMetadatasListCount<Runtime>,TargetCanonicalSpecializedMetadatasListEntry<Runtime>,TargetCanonicalSpecializedMetadatasCachingOnceToken<Runtime>> {....../// The number of stored properties in the struct./// If there is a field offset vector, this is its length.uint32_t NumFields; //记录属性的count/// 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; //记录属性在metadata中的偏移量
TargetValueTypeDescriptor
继承自TargetTypeContextDescriptor
,TargetTypeContextDescriptor
含有三个属性:Name
(类型的名称)、AccessFunctionPtr
(指向此类型的元数据访问函数的指针)和Fields
(指向类型的字段描述符的指针)。
class TargetValueTypeDescriptor: public TargetTypeContextDescriptor<Runtime> {public:static bool classof(const TargetContextDescriptor<Runtime> *cd) {return cd->getKind() == ContextDescriptorKind::Struct ||cd->getKind() == ContextDescriptorKind::Enum;}
};
class TargetTypeContextDescriptor: public TargetContextDescriptor<Runtime> {public:/// The name of the type.// 类型的名称TargetRelativeDirectPointer<Runtime, const char, /*nullable*/ false> 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<Runtime, MetadataResponse(...),/*Nullable*/ true> AccessFunctionPtr;/// A pointer to the field descriptor for the type, if any.// 指向类型的字段描述符的指针TargetRelativeDirectPointer<Runtime, const reflection::FieldDescriptor,/*nullable*/ true> Fields;......
}
TargetTypeContextDescriptor
又继承自基类TargetContextDescriptor
,TargetContextDescriptor
包含两个属性:Flags
(用于表示描述context
的标志,包含kind
和version
)和Parent
(用于表示父类的context
,如果是在顶层,则表示没有父类,则为NULL
)。
/// Base class for all context descriptors.
template<typename Runtime>
struct TargetContextDescriptor {/// Flags describing the context, including its kind and format version.// 用于表示描述context的标志,包含kind和versionContextDescriptorFlags Flags;/// The parent context, or null if this is a top-level context.// 用于表示父类的context,如果是在顶层,则表示没有父类,则为NULLTargetRelativeContextPointer<Runtime> Parent;......
}
从这里开始,TargetStructDescriptor
就已经明了了,我们就可以写出TargetStructDescriptor
的相关结构,同时修正TargetStructMetadata
中的泛型T。
struct TargetStructMetadata {var kind: Intvar typeDescriptor: UnsafeMutablePointer<TargetStructDescriptor>
}struct TargetStructDescriptor {// 用于表示描述context的标志,包含kind和versionvar flags: Int32 // ContextDescriptorFlags Int32// 用于表示父类的context,如果是在顶层,则表示没有父类,则为NULLvar parent: TargetRelativeContextPointer<UnsafeRawPointer> // Relative 相对地址// 类型的名称var name: TargetRelativeDirectPointer<CChar> // Relative 相对地址// 指向此类型的元数据访问函数的指针var accessFunctionPointer: TargetRelativeDirectPointer<UnsafeRawPointer> // Relative 相对地址// 指向类型的字段描述符的指针var fieldDescriptor: TargetRelativeDirectPointer<FieldDescriptor> // Relative 相对地址// 记录属性的countvar numFields: Int32// 记录属性在metadata中的偏移量var fieldOffsetVectorOffset: Int32
}// 下面是一些属性的类型解析
/// Common flags stored in the first 32-bit word of any context descriptor.
// flags 就是 Int32
struct ContextDescriptorFlags {private:uint32_t Value;
}
实现TargetRelativeDirectPointer
对于相对地址TargetRelativeDirectPointer
,我们从源码中搜索TargetRelativeDirectPointer
可得出TargetRelativeDirectPointer
就是RelativeDirectPointer
。
template <typename Runtime, typename Pointee, bool Nullable = true>
using TargetRelativeDirectPointer= typename Runtime::template RelativeDirectPointer<Pointee, Nullable>;
接着在RelativePointer.h
找到RelativeDirectPointer
,发现RelativeDirectPointer
继承自基类RelativeDirectPointerImpl
,其包含一个属性RelativeOffset
(偏移量)。并且其含有通过偏移量获取真实内存的方法。
template <typename T, bool Nullable = true, typename Offset = int32_t,typename = void>
class RelativeDirectPointer;/// A direct relative reference to an object that is not a function pointer.
// offset传入Int32
template <typename T, bool Nullable, typename Offset>
class RelativeDirectPointer<T, Nullable, Offset,typename std::enable_if<!std::is_function<T>::value>::type>: private RelativeDirectPointerImpl<T, Nullable, Offset>
{......
}/// 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<typename T, bool Nullable, typename Offset>
class RelativeDirectPointerImpl {private:/// The relative offset of the function's entry point from *this.Offset RelativeOffset;......// 通过偏移量计算 同时还返回泛型T类型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<PointerTy>(absolute);}......
}/// Apply a relative offset to a base pointer. The offset is applied to the base
/// pointer using sign-extended, wrapping arithmetic.
// 通过偏移量计算
template<typename BasePtrTy, typename Offset>
static inline uintptr_t applyRelativeOffset(BasePtrTy *basePtr, Offset offset) {static_assert(std::is_integral<Offset>::value &&std::is_signed<Offset>::value,"offset type should be signed integer");auto base = reinterpret_cast<uintptr_t>(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;// 指针地址+存放的offset(偏移地址) -- 内存平移获取值return base + extendOffset;
}
那么我们就可以TargetRelativeDirectPointer
的结构:
// 传入泛型Pointee
struct TargetRelativeDirectPointer<Pointee> {var offset: Int32// 通过偏移量计算内存mutating func getmeasureRelativeOffset() -> UnsafeMutablePointer<Pointee> {let offset = self.offsetreturn withUnsafePointer(to: &self) { p in// 使用advanced偏移offset,再重新绑定成Pointee类型return UnsafeMutablePointer(mutating: UnsafeRawPointer(p).advanced(by: numericCast(offset)).assumingMemoryBound(to: Pointee.self))}}
}
同时我们就可以修正TargetStructDescriptor
为:
struct TargetStructDescriptor {// 用于表示描述context的标志,包含kind和versionvar flags: Int32// 用于表示父类的context,如果是在顶层,则表示没有父类,则为NULLvar parent: Int32// 由于不去解析,暂时定义为Int32// 类型的名称var name: TargetRelativeDirectPointer<CChar>// 指向此类型的元数据访问函数的指针var accessFunctionPointer: TargetRelativeDirectPointer<UnsafeRawPointer>// 指向类型的字段描述符的指针var fieldDescriptor: TargetRelativeDirectPointer<FieldDescriptor>// 记录属性的countvar numFields: Int32// 记录属性在metadata中的偏移量var fieldOffsetVectorOffset: Int32
}// TargetRelativeContextPointer暂时不解析,通过源码分析可得暂时解析为Int32
template<typename Runtime,template<typename _Runtime> class Context = TargetContextDescriptor>
using TargetRelativeContextPointer =RelativeIndirectablePointer<const Context<Runtime>,/*nullable*/ true, int32_t,TargetSignedContextPointer<Runtime, Context>>;
FieldDescriptor和FieldRecord
再下一步,我们开始解析FieldDescriptor
,源码中FieldDescriptor
如下:
// 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<const FieldRecord *>(this + 1);}public:const RelativeDirectPointer<const char> MangledTypeName;const RelativeDirectPointer<const char> Superclass;FieldDescriptor() = delete;const FieldDescriptorKind Kind;const uint16_t FieldRecordSize;const uint32_t NumFields;......// 获取所有属性,每个属性用FieldRecord封装llvm::ArrayRef<FieldRecord> getFields() const {return {getFieldRecordBuffer(), NumFields};}......
}// FieldDescriptorKin就是 Int16
enum class FieldDescriptorKind : uint16_t {......
}
FieldRecord
在源码中的结构为:
class FieldRecord {const FieldRecordFlags Flags;public:const RelativeDirectPointer<const char> MangledTypeName;const RelativeDirectPointer<const char> FieldName;......
}// Field records describe the type of a single stored property or case member
// of a class, struct or enum.
// FieldRecordFlags 就是Int32
class FieldRecordFlags {using int_type = uint32_t;......
}
fieldOffsetVectorOffset计算偏移量
最后还有fieldOffsetVectorOffset
(记录属性在metadata
中的偏移量)的计算,来获取属性再metadata
中的偏移量。源码中能得到的资料是:
// StoredPointer 是Int32 即会返回一个Int32/// Get a pointer to the field offset vector, if present, or null.const StoredPointer *getFieldOffsets() const {assert(isTypeMetadata());auto offset = getDescription()->getFieldOffsetVectorOffset();if (offset == 0)return nullptr;auto asWords = reinterpret_cast<const void * const*>(this);return reinterpret_cast<const StoredPointer *>(asWords + offset);}
但是以这个逻辑去处理,获取的数据是不对的,所以我从HandyJSON
的源码中找到了这个:
// 当时64位是 offset 会乘以2
return Int(UnsafePointer<Int32>(pointer)[vectorOffset * (is64BitPlatform ? 2 : 1) + $0])
分析到这里,我们就得到了一个比较清晰地结构线,如下:
// 通过偏移量计算内存地址 传入泛型Pointee
struct TargetRelativeDirectPointer<Pointee> {var offset: Int32// 通过偏移量计算内存mutating func getmeasureRelativeOffset() -> UnsafeMutablePointer<Pointee> {let offset = self.offsetreturn withUnsafePointer(to: &self) { p in// 使用advanced偏移offset,再重新绑定成Pointee类型return UnsafeMutablePointer(mutating: UnsafeRawPointer(p).advanced(by: numericCast(offset)).assumingMemoryBound(to: Pointee.self))}}
}struct TargetStructMetadata {var kind: Intvar typeDescriptor: UnsafeMutablePointer<TargetStructDescriptor>
}struct TargetStructDescriptor {var flags: Int32var parent: Int32var name: TargetRelativeDirectPointer<CChar>var accessFunctionPointer: TargetRelativeDirectPointer<UnsafeRawPointer>var fieldDescriptor: TargetRelativeDirectPointer<FieldDescriptor>var numFields: Int32var fieldOffsetVectorOffset: Int32func getFieldOffsets(_ metadata: UnsafeRawPointer) -> UnsafePointer<Int32> {print(metadata)return metadata.assumingMemoryBound(to: Int32.self).advanced(by: numericCast(self.fieldOffsetVectorOffset) * 2)}// 计算元型时使用var genericArgumentOffset: Int {return 2}
}struct FieldDescriptor {var MangledTypeName: TargetRelativeDirectPointer<CChar>var Superclass: TargetRelativeDirectPointer<CChar>var kind: UInt16var fieldRecordSize: Int16var numFields: Int32var fields: FieldRecordBuffer<FieldRecord>
}struct FieldRecord {var fieldRecordFlags: Int32var mangledTypeName: TargetRelativeDirectPointer<CChar>var fieldName: TargetRelativeDirectPointer<UInt8>
}// 获取FieldRecord
struct FieldRecordBuffer<Element> {var element: Elementmutating func buffer(n: Int) -> UnsafeBufferPointer<Element> {return withUnsafePointer(to: &self) {let ptr = $0.withMemoryRebound(to: Element.self, capacity: 1) { start inreturn start}return UnsafeBufferPointer(start: ptr, count: n)}}mutating func index(of i: Int) -> UnsafeMutablePointer<Element> {return withUnsafePointer(to: &self) {return UnsafeMutablePointer(mutating: UnsafeRawPointer($0).assumingMemoryBound(to: Element.self).advanced(by: i))}}
}
代码的验证
下面我们就代码来验证我们得到的这个结构。
protocol BrigeProtocol {}extension BrigeProtocol {// 通过协议重新绑定类型 返回出去static func get(from pointor: UnsafeRawPointer) -> Any {// Self就是真实的类型pointor.assumingMemoryBound(to: Self.self).pointee}
}struct BrigeMetadataStruct {let type: Any.Typelet witness: Int
}func custom(type: Any.Type) -> BrigeProtocol.Type {let container = BrigeMetadataStruct(type: type, witness: 0)let cast = unsafeBitCast(container, to: BrigeProtocol.Type.self)return cast
}
// LLPerson结构体
struct LLPerson {var age: Int = 18var name: String = "LL"var nameTwo: String = "LLLL"
}
// 创建一个实例
var p = LLPerson()
// LLPerson的metadata按位塞入TargetStructMetadata这个metadata中,LLPerson.self就是UnsafeMutablePointer<TargetStructMetadata>.self
let ptr = unsafeBitCast(LLPerson.self as Any.Type, to: UnsafeMutablePointer<TargetStructMetadata>.self)// 拿到结构体名称
let namePtr = ptr.pointee.typeDescriptor.pointee.name.getmeasureRelativeOffset()
print("当前 struct name: \(String(cString: namePtr))")
// 拿到属性个数
let numFields = ptr.pointee.typeDescriptor.pointee.numFields
print("当前类属性个数: \(numFields)")// 拿到属性再metadata中的偏移量
let offsets = ptr.pointee.typeDescriptor.pointee.getFieldOffsets(UnsafeRawPointer(ptr).assumingMemoryBound(to: Int.self))print("----------- start fetch field -------------")for i in 0..<numFields {// 获取属性名let fieldName = ptr.pointee.typeDescriptor.pointee.fieldDescriptor.getmeasureRelativeOffset().pointee.fields.index(of: Int(i)).pointee.fieldName.getmeasureRelativeOffset()print("----- field \(String(cString: fieldName)) -----")// 拿到属性对应的偏移量 按字节偏移的let fieldOffset = offsets[Int(i)]print("\(String(cString: fieldName)) 的偏移量是:\(fieldOffset)字节")// 这是swift混写过的类型名称 需要把它转成真正的类型名称let typeMangleName = ptr.pointee.typeDescriptor.pointee.fieldDescriptor.getmeasureRelativeOffset().pointee.fields.index(of: Int(i)).pointee.mangledTypeName.getmeasureRelativeOffset()
// print("\(String(cString: typeMangleName))")let genericVector = UnsafeRawPointer(ptr).advanced(by: ptr.pointee.typeDescriptor.pointee.genericArgumentOffset * MemoryLayout<UnsafeRawPointer>.size).assumingMemoryBound(to: Any.Type.self)// 需要用到这个库函数 swift_getTypeByMangledNameInContext 传递四个参数let fieldType = swift_getTypeByMangledNameInContext(typeMangleName, // 混写过后的名称256, // 混写过后的名称信息长度,需要计算 HandyJSON中直接 256UnsafeRawPointer(ptr.pointee.typeDescriptor), // 上下文 typeDescriptor中UnsafeRawPointer(genericVector).assumingMemoryBound(to: Optional<UnsafeRawPointer>.self)) //当前的泛型参数 还原符号信息// 将fieldType按位塞入Anylet type = unsafeBitCast(fieldType, to: Any.Type.self)// 通过协议桥接获取我们的真实类型信息let value = custom(type: type)//获取实例对象p的指针 需要转换成UnsafeRawPointer 并且绑定成1字节即Int8类型,//因为后面是按字节计算偏移量的,不转换,会以结构体的长度偏移let instanceAddress = withUnsafePointer(to: &p){return UnsafeRawPointer($0).assumingMemoryBound(to: Int8.self)}print("fieldTyoe: \(type) \nfieldValue: \(value.get(from: instanceAddress.advanced(by: Int(fieldOffset))))")
}print("----------- end fetch field -------------")
打印信息:
从内存地址我们也可以看出属性的布局信息。
Swift 类似HandyJSON解析Struct相关推荐
- Swift之深入解析如何避免单元测试中的强制解析
一.前言 强制解析(使用 !)是 Swift 语言中不可或缺的一个重要特点(特别是和 Objective-C 的接口混合使用时),它回避了一些其他问题,使得 Swift 语言变得更加优秀. 比如在我的 ...
- swift php json解析,Swift 4.0 | JSON数据的解析和编码
文 / 菲拉兔 自己撸的图 要求: Platform: iOS8.0+ Language: Swift4.0 Editor: Xcode9 [问题补充2017-09-28] 最近我发现了一个问题:在S ...
- iOS swift Alamofire+HandyJSON网络框架封装
iOS swift Alamofire+HandyJSON网络框架封装 我们在学习Objective_C时使用的网络框架是AFNetworking+MJExtension,而在swift中Alamof ...
- Swift之深入解析如何使用Xcode和LLDB v2修改UI元素
一.前言 在上一篇博客中,已经详细地介绍如何使用 LLDB 表达式修改 UI 元素,具体请参考:Swift之深入解析如何将代码添加为自定义LLDB命令. 在这篇博客中,将继续讨论相同的问题需求,并将重 ...
- Swift之深入解析如何进行多重条件排序
一.前言 在一个条件或者单个属性上进行排序非常简单, Swift 本身就有相关的功能. 如下所示,对 int 数组进行排序的例子: let numbers = [3, 5, 6, 1, 8, 2] l ...
- Swift之深入解析“对象”的底层原理
一.Swift 编译简介 Swift 的编译环境配置和编译流程,请参考我之前的博客:Swift之源码编译的环境搭建和编译流程: 新建一个 Swift 工程,在 main.swift 中创建一个 YDW ...
- Swift中Class和Struct异同
Swift 中类和结构体有很多共同点.共同处在于: 定义属性用于存储值 定义方法用于提供功能 定义下标操作使得可以通过下标语法来访问实例所包含的值 定义构造器用于生成初始化值 通过扩展以增加默认实现的 ...
- Swift之深入解析类和结构体的本质
一.类和结构体的异同 Swift中,类和结构体有许多相似之处,但也有不同.内存分配可以分为堆区(Heap)和栈区(Stack),由于栈区内存是连续的,内存的分配和销毁是通过入栈和出栈操作进行的,速度要 ...
- Swift之深入解析可选链的功能和使用
一.什么是可选链? 可选链(Optional Chaining)是一种可以请求和调用属性.方法和子脚本的过程,用于请求或调用的目标可能为nil. 可选链返回两个值: 如果目标有值,调用就会成功,返回该 ...
最新文章
- 自己开发的Grid组件 针对IOS的
- C#字符ASCII码学习经验
- better-scroll:angularJs中用better-scroll封装一个滚动的指令
- PyQt5 Introduction and components
- Wireshark个人实战总结
- SpringBoot学习笔记001--创建第一个spring boot应用
- VsCode crtl + 鼠标右键 python代码无法跳转
- 定义字符串 && 字符串数组
- C++之调用C的so
- MATLAB数值微积分与方程求解
- Java实现滑块拼图验证码校验
- AtCoder Beginner Contest 158 D.String Formation
- 一只程序员的成长与思考
- 怎么做二维码?二维码制作的简单方法
- android 目录详解,Android源码目录结构详解
- 交互式多模型-无迹卡尔曼滤波IMM-UKF算法matlab实现(跟踪场景二)
- 关于:File.separator ( 详解 )
- ecshop mysql 30秒_ecshop数据库字段说明汇总
- 小游戏《塔防》开发(二)
- 气泡shader_Android超简单气泡效果
热门文章
- GEE账号注册--踩雷笔记(看后秒过)
- 红蓝对抗-钓鱼邮件模板一
- 在Java中实现两数相乘
- h5 input标签使用正则表达式限制输入
- java爬山问题,关于java:不爬山不拍照设计模式入门到入坑第一课
- java 中FreeMaker的使用(excel、word),涉及解压缩
- 使用xlsx.full.min.js读取excel表格数据
- Ceph集群修改IP地址
- 微信小程序:Component “页面路径“ does not have a method “ 方法名“ to handle event “tap“.
- linux怎么开启telnet命令,linux如何开启telnet服务?linux开启telnet服务的方法