在swift源碼中有這樣一段代碼旱爆,可以看到所有的類型都是有元類型的,
類
、枚舉
冲泥、結構體
都被已經被定義了,下面就通過mirror
來還原TargetStructMetadata結構驹碍。
1壁涎、Mirror
struct Teacher{
var age = 18
var name = "henry"
var height = 1.8
var hobby = "woman"
}
let t = Teacher()
let mirror = Mirror(reflecting: t)
for pro in mirror.children{
print("\(pro.label):\(pro.value)")
}
為什么Mirror可以獲取到結構體的屬性名稱?
1.1 Mirror源碼
public struct Mirror {
//初始化方法
public init(reflecting subject: Any) {
if case let customized as CustomReflectable = subject {
self = customized.customMirror
} else {
self = Mirror(internalReflecting: subject)
}
}
}
1.2 Mirror初始化
extension Mirror {
internal init(internalReflecting subject: Any,
subjectType: Any.Type? = nil,
customAncestor: Mirror? = nil)
{
// type(of:)獲取到subject的真實類型
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
}
if let customAncestor = customAncestor {
if superclass == customAncestor.subjectType {
return customAncestor
}
if customAncestor._defaultDescendantRepresentation == .suppressed {
return customAncestor
}
}
return Mirror(internalReflecting: subject,
subjectType: superclass,
customAncestor: customAncestor)
}
// 獲取并解析顯示的樣式怔球,并設置Mirror的其他屬性
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
}
-
type(of:)
獲取到subject的真實類型
2、TargetStructMetadata結構
源碼中繼承關系比較復雜浮还,因為設計到繼承和一些模板類竟坛,所以只放出一張結構圖:
轉換為代碼形式:
struct StructMetaData{
var kind : Int32
var description : UnsafePointer<StructDescriptor>
}
struct StructDescriptor {
let flags: Int32
let parent: Int32
var name: RelativePointer<CChar>
var AccessFunctionPtr: RelativePointer<UnsafeRawPointer>
var Fields: RelativePointer<FieldDescriptor>
var NumFields: Int32
var FieldOffsetVectorOffset: Int32
}
struct FieldDescriptor {
var MangledTypeName: RelativePointer<CChar>
var Superclass: RelativePointer<CChar>
var kind: UInt16
var fieldRecordSize: Int16
var numFields: Int32
var fields: FieldRecord //連續(xù)的存儲空間
}
struct FieldRecord {
var Flags: Int32
var MangledTypeName: RelativePointer<CChar>
var FieldName: RelativePointer<CChar>
}
struct RelativePointer<T> {
var offset:Int32
mutating public func get() -> UnsafePointer<T> {
let offSet = self.offset
return withUnsafePointer(to: &self){ ptr in
UnsafeRawPointer(ptr).advanced(by: numericCast(offSet)).assumingMemoryBound(to: T.self)
}
}
}
3、還原結構
根據源碼要先獲取元類指針,2種方式均可钧舌。swift指針使用起來有點難度(攤牌了我是個菜雞流码。。延刘。)swift底層探索 06 - 指針簡單使用
- withUnsafeMutablePointer
var type = Teacher.self
//指向元類指針的指針
let a = withUnsafeMutablePointer(to: &type) { ptr in
UnsafeRawPointer(ptr).bindMemory(to: UnsafeMutablePointer<StructMetaData>.self, capacity: 1)
}
// 獲取元類指針
let metaPtr = a.pointee
- 注意:
a是 : 指向元類指針的指針
- unsafeBitCast按位強轉
let metaPtr = unsafeBitCast(Teacher.self as Any.Type, to: UnsafePointer<StructMetaData>.self)
- unsafeBitCast需要兩個參數的內存大小相同漫试。必須使用:
Teacher.self as Any.Type
進行轉換,因為根據測試發(fā)現Teacher.self
獲取內存大小為0
(這個地方是真的坑)碘赖,如下圖:
Teacher.self as Any.Type 動作
根據sil查看發(fā)現
Teacher.self as Any.Type
在底層的操作驾荣;
- sil官方文檔
- 創(chuàng)建
T 的元類型普泡。
3.1 獲取結構體名
let structName = metaPtr.pointee.description.pointee.name.get()
let structNameStr = String(cString: structName)
print("獲取結構體名:\(structNameStr)")
輸出:
3.2 獲取內部屬性個數
let cnt = metaPtr.pointee.description.pointee.NumFields
print("獲取結構體屬性個數:\(cnt)")
輸出:
3.3 獲取所有屬性
let fieldsPtr = b.pointee.description.pointee.Fields.get()
for i in 0..<cnt {
let record = withUnsafePointer(to: &fieldsPtr.pointee.fields) {
UnsafeMutablePointer(mutating: UnsafeRawPointer($0).assumingMemoryBound(to: FieldRecord.self).advanced(by: numericCast(i)))
}
let recordNameStr = String(cString: record.pointee.FieldName.get())
let manNameStr = String(cString: record.pointee.MangledTypeName.get())
print("類型名:\(recordNameStr) ---- 類型類型:\(manNameStr)")
}
輸出:
- si : 其實就是Int
- ss : 其實就是String
- sd : 其實就是Double