一、AnyObject & .self、AnyClass & Any、self & Self、获取类型
1.1 AnyObject & .self
AnyObject代表任意类的实例,类的类型,仅类遵守的协议。
class CTTeacher{
var age = 18
}
var t = CTTeacher()
var t1:AnyObject = t.self
var t2:AnyObject = t.self.self.self.self
var t3:AnyObject = CTTeacher.self
print("end")
调试看一下x86源码:
结论1:
T.self:当
T是实例对象时,T.self返回的就是实例对象本身;当T是类时,T.self返回的就是元类型。
再来看看AnyObject仅类遵守的协议是啥意思
class CTTeacher{
var age = 18
}
protocol MyProtocal:AnyObject {
}
extension CTTeacher:MyProtocal{}
但如果把class改为struct:
结论2:
AnyObject:当协议继承自AnyObject时,这个协议只能够被
class遵循,也就是仅类遵守的协议
1.2 AnyClass
AnyClass: 代表任意实例的类型【注意是类型,不是实例】
通过这个代码提示能看出,
AnyClass是AnyObject.Type类型的,具体来说:
class CTTeacher{
var age = 18
}
var t:AnyClass = CTTeacher.self
1.3 Any
Any: 代表任意类型,包括方法funcation类型、可选值optional类型
下面这个数组中的1,由于数组内容类型不同,所以需要用Any来指定数组类型,可以用任何类型,换成Anyobject就不行,因为1是int类型,基本类型不属于AnyObject类型。
var array:[Any] = [1, "CCT"]
AnyClass属于Any,Any代表了任意类型
class CTTeacher{
var age = 18
}
var t:AnyClass = CTTeacher.self
var s:Any = t
1.4 self & Self
self
class CTTeacher {
var age = 18
func test(){
print(self) //这个self是指 实例对象
}
static func test1(){
print(self) //这个self是指 类本身 也就是元类型
}
}
let t = CTTeacher()
t.test()
CTTeacher.test1()
Self
用法一:作为方法返回值 指当前类的实例对象
class CTTeacher {
func test() -> Self{ //Self作为方法返回值 指当前类的实例对象
return self
}
}
用法二:作为协议方法返回值 指遵循这个协议的[类]
protocol MyProtocol {
func get() -> Self //Self作为协议方法返回值 指遵循这个协议的[类]
}
class CTPerson:MyProtocol{
func get() -> Self {
return self
}
}
用法三:类中指代本类
class CTStudent{
//类型属性
static let age = 0
//存储属性
let age1 = age
var age2 = age
lazy var age3 = Self.age //Self指本类 不能写成self.age
}
1.5 获取类型
1.5.1 [ .self ] & [ .type ]
.type可以直接理解为:xx的类型
这儿有个逻辑容易搞混:
通过 xxx.self 得到 xxxx.type 类型
class CTTeacher{
var age = 18
}
CTTeacher.self //这句代码得到:CTTeacher.type这个类型
1.5.2 type(of:)
type(of:) 获取一个值的动态类型
静态类型(static type) 是在编译时确定的,动态类型(dynamic type) 是在运行时确定的
var age = 10
var name = "CCT"
//value的静态类型是Any,也就是语法层面,我们指定的类型是Any
func test(_ value:Any){
print("value:\(value),type:\(type(of: value))")
}
test(age)
test(name)
Self在使用的时候,返回的也是实际类型,我们可以理解为返回的就是type(of:) 实际类型
二、Swift Runtime
class LGTeacher {
var age: Int = 18
func teach(){
print("teach")
}
}
let t = LGTeacher()
func test(){
var methodCount:UInt32 = 0
let methodlist = class_copyMethodList(LGTeacher.self, &methodCount)
for i in 0..<numericCast(methodCount) {
if let method = methodlist?[i]{
let methodName = method_getName(method);
print("⽅法列表:\(String(describing: methodName))")
}else{
print("not found method");
}
}
var count:UInt32 = 0
let proList = class_copyPropertyList(LGTeacher.self, &count)
for i in 0..<numericCast(count) {
if let property = proList?[i]{
let propertyName = property_getName(property);
print("属性成员属性:\(String(utf8String: propertyName)!)")
}else{
print("没有找到你要的属性");
}
}
}
test()
1、运行一下,会发现什么都没打印。
结论1:对于纯 Swift 类来说,⽅法和属性不@objc修饰符的情况下,不具备Runtime特性
2、在属性和方法前加上@objc:
class LGTeacher {
var age: Int = 18
func teach(){
print("teach")
}
}
运行:
结论2:对于纯 Swift 类,⽅法和属性前添加
@objc标识,可以通过Runtime API拿到,但是在OC中是没法进⾏调度的
3、把LGTeacher继承自NSObject,去掉@objc标识:
class LGTeacher:NSObject {
var age: Int = 18
func teach(){
print("teach")
}
}
运行:
只拿到init方法
再加上@objc标识:
class LGTeacher:NSObject {
@objc var age: Int = 18
@objc func teach(){
print("teach")
}
}
运行:
结论3:对于继承⾃
NSObject类来说,如果要通过Runtime API获取当前的属性和⽅法,必须在其声明前添加@objc关键字。
纯swift类没有动态性,但在⽅法、属性前添加dynamic修饰,可获得动态性。
继承⾃NSObject的swift类,其继承⾃⽗类的⽅法具有动态性,其它⾃定义⽅法、属性想要获得动态性,需要添加dynamic修饰。
若⽅法的参数、属性类型为swift特有、⽆法映射到objective-c的类型(如Character、Tuple),则此⽅法、属性⽆法添加dynamic修饰(编译器报错)
三、Mirror基本用法
构建⼀个Mirror实例,这⾥传⼊的参数是 Any,也就意味着当前可以是类,结构体,枚举等
let mirror = Mirror(reflecting: <#Any#>)
class LGTeacher {
var age: Int = 18
func teach(){
print("teach")
}
}
let t = LGTeacher()
let mirror = Mirror(reflecting: t)
//遍历 children 属性,这是⼀个集合
for pro in mirror.children {
//直接通过 label 输出当前的名称,value 输出当前反射的值
print("\(pro.label) - \(pro.value)")
}
3.1 Mirror使用案例
protocol CTJsonMap{
func jsonMap() -> Any
}
extension CTJsonMap{
func jsonMap() -> Any {
let mirror = Mirror(reflecting: self)
guard !mirror.children.isEmpty else {
return self
}
var result:[String: Any] = [:]
for child in mirror.children {
if let value = child.value as? CTJsonMap {
if let key = child.label {
result[key] = value.jsonMap()
}
else{
print("No keys")
}
}
else{
print("value not conform CTJsonMap")
}
}
return result
}
}
//使用:
class CTTeacher {
var age: Int = 18
func teach(){
print("teach")
}
}
extension CTTeacher:CTJsonMap{}
extension String:CTJsonMap{}
extension Int:CTJsonMap{}
let t = CTTeacher()
print(t.jsonMap()) // ["age": 18]
3.2 错误抛出与捕获 throws & try
Swift 提供 Error协议 来标识当前应⽤程序发⽣错误的情况
结合上面的案例,自定义一个错误类型:[继承自Error的枚举]
//继承自Error
enum JSONMapError: Error{
case emptyKey
case notConformProtocol
}
修改部分代码:
try try? try!
四、Mirror源码解析
4.1 Mirror源码
源代码Mirror.swift,定位到初始化的方法
这⾥接受⼀个Any类型的参数,这⾥判断subject是否遵循了自定义协议customReflectable协议,如果是,就直接调⽤customMirror, 否则就调用Mirror默认的初始化方法Mirror(internalReflecting: subject)。
4.2 CustomReflectable协议
class CTTeacher: CustomReflectable {
var age: Int
var name: String
init(age: Int, name: String) {
self.age = age
self.name = name
}
var customMirror: Mirror{
let info = KeyValuePairs<String, Any>.init(dictionaryLiteral: ("age", age),("name", name))
let mirror = Mirror.init(self, children: info, displayStyle: .class, ancestorRepresentation: .generated)
return mirror
}
}
CustomReflectable协议:这个协议只有一个属性 customMirror,返回的就是一个Mirror实例,它能让我们自定义反射的信息
对比一下不遵循
CustomReflectable协议的:
结论:遵循了
CustomReflectable协议,我们可以自定义反射的信息,在LLDBpo调试的时候,会输出对象的内存地址和反射出的属性及属性值
注意初始化方法里面的 displayStyle: 要根据实际传入对应的值
回到Mirror的初始化方法,我们全局搜一下Mirror(internalReflecting这个初始化方法,来到ReflectionMirror.swift文件
看看 _getNormalizedType(),以及 _getChildCount()以及这两个方法的定义:
发现 @_silgen_name 这个神奇玩意儿,并且俩方法都没有具体实现。
sigen_name()
@_silgen_name是⼀个编译器字段,是Swift的⼀个隐藏符号,作⽤是将某个C/C++语⾔函数直接映射为Swift函数。
比如在C中定义一个函数,如果想在Siwft中使用这个函数,那么先在c文件中定义这个函数:
然后在Swift中使用@_silgen_name关键字:
回到_getNormalizedType()以及 _getChildCount()方法,那么也就是说,调用这俩函数实际上调用的是其对应的c函数。swift_reflectionMirror_normalizedType swift_reflectionMirror_count
全局搜swift_reflectionMirror_normalizedType,可以在ReflectionMirror.mm文件中找到
我们再找到call方法
这⾥是⼀个回调函数,回调的具体数据都是由ReflectionMirrorImpl结构体实现
下面有一个类型的匹配判断,调用上面那个auto call()方法,传入对应类型的impl
ReflectionMirrorImpl结构体是一个抽象类,意味着不同类型的反射都需要去实现。
那么class, struct, enum, Tuple它们都有对应的具体实现,拿struct为例子具体看看Mirror是如何获取这些数据的:
是否可以反射:通过Metadata中的getDescription查询字段isReflectable
获取属性数量:通过Metadata中的getDescription查询字段NumFields
获取具体属性:
可以看到是这⾥通篇都是通过 Metadata,getDescription(),FieldDescrition
这⼏个东⻄来去实现的,⼀个是当前类型的元数据,⼀个是当前类型的描述,⼀个是对当前类型属性的
描述。
在Swift源码Metadata.h中找到TargetEnumMetadata
可以看到它继承于
TargetValueMetadata,点进去:
它有一个
Description字段,是TargetValueTypeDescriptor类型。
它继承自TargetMetadata,再点进去:
有一个kind类型。
ok,到这里我们可以推断出EnumMetadata在Swift中长这样:
struct TargetEnumMetadata{
var kind: Int
var typeDescriptor: UnsafeRawPointer //其中这⾥指向的是descriptor
}
接下来可以通过追踪Descriptor的继承链,来还原它的结构:
struct TargetEnumDescriptor{
var flags: Int32
var parent: TargetRelativeDirectPointer<UnsafeRawPointer>
var name: TargetRelativeDirectPointer<CChar>
var accessFunctionPointer: TargetRelativeDirectPointer<UnsafeRawPointer>
var fieldDescriptor: TargetRelativeDirectPointer<UnsafeRawPointer>
var NumPayloadCasesAndPayloadSizeOffset: UInt32
var NumEmptyCases: UInt32
}
那么这里面有好几个属性是TargetRelativeDirectPointer这个类型的,它是什么玩意儿?
点击进去可以发现,它是一个模板类,接收三个参数,⼀个是 Runtime , ⼀个是Pointee , 一个是Bool 类型默认为 True。
点击RelativeDirectPointer进去看:
这里的T就是我们进来的类型,Offset就是int32_t类型。在swift中引用一个Object有两种情况:
- 当前 Object 的引⽤是⼀个 absolute 指针,就像我们的对象⼀样
- 当前指针存储的是相对引⽤,相对引⽤指的是当前引⽤的内存地址到当前对象的内存地址的距离
OK,在TargetEnumDescriptor中的TargetRelativeDirectPointer的属性类型存的都是相对地址信息。
我们尝试用swift还原TargetRelativeDirectPointer:
struct TargetRelativeDirectPointer<Pointee>{
var offset: Int32
mutating func getmeasureRelativeOffset() -> UnsafeMutablePointer<Pointee>{
let offset = self.offset
return withUnsafePointer(to: &self) { p in
return UnsafeMutablePointer(mutating: UnsafeRawPointer(p).advanced(by: numericCast(offset)).assumingMemoryBound(to: Pointee.self))
}
}
}
我们把TargetRelativeDirectPointer的定义和以上代码结合起来看:
以上都串起来:
struct TargetRelativeDirectPointer<Pointee>{
var offset: Int32
mutating func getmeasureRelativeOffset() -> UnsafeMutablePointer<Pointee>{
let offset = self.offset
return withUnsafePointer(to: &self) { p in
return UnsafeMutablePointer(mutating: UnsafeRawPointer(p).advanced(by: numericCast(offset)).assumingMemoryBound(to: Pointee.self))
}
}
}
struct TargetEnumDescriptor{
var flags: Int32
var parent: TargetRelativeDirectPointer<UnsafeRawPointer>
var name: TargetRelativeDirectPointer<CChar>
var accessFunctionPointer: TargetRelativeDirectPointer<UnsafeRawPointer>
var fieldDescriptor: TargetRelativeDirectPointer<UnsafeRawPointer>
var NumPayloadCasesAndPayloadSizeOffset: UInt32
var NumEmptyCases: UInt32
}
struct TargetEnumMetadata{
var kind: Int
var typeDescriptor: UnsafeMutablePointer<TargetEnumDescriptor>
}
来个demo,跑一下:
enum TerminalChar {
case Plain(Bool)
case Bold
case Empty
case Cursor
}
var e = TerminalChar.self
let ptr = unsafeBitCast(TerminalChar.self as Any.Type, to: UnsafeMutablePointer<TargetEnumMetadata>.self)
let namePtr = ptr.pointee.typeDescriptor.pointee.name.getmeasureRelativeOffset()
print(String(cString: namePtr))
print(ptr.pointee.typeDescriptor.pointee.NumPayloadCasesAndPayloadSizeOffset)
print(ptr.pointee.typeDescriptor.pointee.NumEmptyCases)
这里面unsafeBitCast解释一下:
var age = 10
var age1 = unsafeBitCast(age, to: Double.self)
print(age1)
ok,所以上面demo的代码就是:(它俩本质上是一样的类型,所以这么转没问题)
再来看一下Class:
class的Metadata之前分析过,直接拿来用。然后它的typeDescriptor也还原为TargetClassDescriptor
struct TargetClassMetadata{
var kind: Int
var superClass: Any.Type
var cacheData: (Int, Int)
var data: Int
var classFlags: Int32
var instanceAddressPoint: UInt32
var instanceSize: UInt32
var instanceAlignmentMask: UInt16
var reserved: UInt16
var classSize: UInt32
var classAddressPoint: UInt32
var typeDescriptor: UnsafeMutablePointer<TargetClassDescriptor>
var iVarDestroyer: UnsafeRawPointer
}
struct TargetClassDescriptor{
var flags: Int32
var parent: Int32
var name: TargetRelativeDirectPointer<CChar>
var accessFunctionPointer: TargetRelativeDirectPointer<UnsafeRawPointer>
var fieldDescriptor: TargetRelativeDirectPointer<FieldDescriptor>
var superClassType: TargetRelativeDirectPointer<CChar>
var metadataNegativeSizeInWords: Int32
var metadataPositiveSizeInWords: Int32
var numImmediateMembers: Int32
var numFields: Int32
var fieldOffsetVectorOffset: Int32 //每一个属性值距离当前实例对象地址的偏移量
func getFieldOffsets(_ metadata: UnsafeRawPointer) -> UnsafePointer<Int>{
return metadata.assumingMemoryBound(to: Int.self).advanced(by: numericCast(self.fieldOffsetVectorOffset))
}
var genericArgumentOffset: Int {
return 2
}
}
来个demo跑一下:
class LGTeacher{
var age: Int = 18
var name: String = "Kody"
}
var t = LGTeacher()
let ptr = unsafeBitCast(LGTeacher.self as Any.Type, to: UnsafeMutablePointer<TargetClassMetadata>.self)
let namePtr = ptr.pointee.typeDescriptor.pointee.name.getmeasureRelativeOffset()
print("current class name: \(String(cString: namePtr))")
let numFileds = ptr.pointee.typeDescriptor.pointee.numFields
print("当前类属性的个数: \(numFileds)")
let superClassType = ptr.pointee.typeDescriptor.pointee.superClassType.getmeasureRelativeOffset()
print("current superClassType: \(String(cString: superClassType))")
let offsets = ptr.pointee.typeDescriptor.pointee.getFieldOffsets(UnsafeRawPointer(ptr).assumingMemoryBound(to: Int.self))
//print("======= start fetch filed ======")
for i in 0..<numFileds{
//取得字段名
let fieldDespritor = ptr.pointee.typeDescriptor.pointee.fieldDescriptor.getmeasureRelativeOffset().pointee.fields.index(of: Int(i)).pointee.fieldName.getmeasureRelativeOffset()
print("--- filed: \(String(cString: fieldDespritor)) info begin ----")
let fieldOffset = offsets[Int(i)]
//取得属性名
//Int ,String
let typeMangleName = ptr.pointee.typeDescriptor.pointee.fieldDescriptor.getmeasureRelativeOffset().pointee.fields.index(of: Int(i)).pointee.mangledTypeName.getmeasureRelativeOffset()
print("typeManglename:\(typeMangleName)")
let genericVector = UnsafeRawPointer(ptr).advanced(by: ptr.pointee.typeDescriptor.pointee.genericArgumentOffset * MemoryLayout<UnsafeRawPointer>.size).assumingMemoryBound(to: Any.Type.self)
//HandJSON
// swift_getTypeByMangledNameInContext 是c函数,要使用它有两种方法:
// 1是 @_silgen_name("swift_getTypeByMangledNameInContext") 符号映射信息
// 2是 直接在c++头文件中声明这个方法名
let fieldType = swift_getTypeByMangledNameInContext(
typeMangleName,
256,
UnsafeRawPointer(ptr.pointee.typeDescriptor),
UnsafeRawPointer(genericVector)?.assumingMemoryBound(to: Optional<UnsafeRawPointer>.self))
// print(fieldType as Any)
//比较难理解,HandJSON
let type = unsafeBitCast(fieldType, to: Any.Type.self)
print(type)
let instanceAddress = Unmanaged.passUnretained(t as AnyObject).toOpaque()
let value = customCast(type: type)
print("fieldType:\(type) \nfieldValue: \(value.get(from: instanceAddress.advanced(by: fieldOffset))) ")
//
// print("--- filed: \(String(cString: fieldDespritor)) info end ---- \n")
}
protocol BrigeProtocol {}
extension BrigeProtocol {
static func get(from pointer: UnsafeRawPointer) -> Any {
pointer.assumingMemoryBound(to: Self.self).pointee
}
}
//协议的Metadata:
//协议见证表
struct BrigeProtocolMetadata {
let type: Any.Type
let witness: Int
}
func customCast(type: Any.Type) -> BrigeProtocol.Type {
var container = BrigeProtocolMetadata(type: type, witness: 0)
var protocolType = BrigeProtocol.Type.self
var cast = unsafeBitCast(container, to: BrigeProtocol.Type.self)
return cast
}