AnyObject、Any、AnyClass、Self 、self 介绍
AnyObject
-
AnyObject
:代表任意类的instance
(实例类型) 、类的类型 、仅类遵守的协议。
class CXTeacher {
var age = 18
}
var t = CXTeacher()
// 代表当前的实例类型
var t1: AnyObject = t
// 代表类的类型
var t2: AnyObject = CXTeacher.self
这里可以看到,当协议继承 AnyObject
的时候,该协议只能被类遵循。
T.self
-
T.self
:T
是实例对象,当前T.self
返回的就是它的本身;如果T
是类,T.self
返回的就是元类型。
class CXTeacher {
var age = 18
}
var t = CXTeacher()
// t.self 是实例对象
var t1 = t.self
// t.self.self 也是实例对象
var t2 = t.self.self
// CXTeacher.self 是元类型,通过汇编代码也可以看到
var t3 = CXTeacher.self
self
class CXTeacher {
var age = 18
func test() {
// self 是当前实例对象
print(self)
}
static func test2() {
// self 是 CXTeacher 这个类型本身
print(self)
}
}
这里在实例方法 test
中 self
代表当前实例对象,在类型方法 test2
中 self
代表的是 CXTeacher
这个类型本身。
Self
- 作为返回值使用
class CXTeacher {
var age = 18
func test() -> Self {
return self
}
static func test2() {
// self 是 CXTeacher 这个类型本身
print(self)
}
}
这里 Self
并没有别的特别的用意,只是为了让我们方便使用当前的类型,这里 Self
作为方法的返回类型,返回的是 self
。
- 在协议中使用
这里 Self
代表遵循此协议的类型。
- 访问类型属性时使用
class CXTeacher {
static let age = 18
lazy var age1 = Self.age
}
当我们定义一个类型属性,想访问该属性的时候可以用 Self.
。
Any
-
Any
: 代表任意类型,包括funcation
类型或者Optional
类型,Any
相对于AnyObject
代表的范围更广泛一点。
这里可以看到,array1
的时候会提示 1 不属于 AnyObject
类型。
AnyClass
-
AnyClass
:代表任意实例的类型。
这里可以看到 AnyClass
是 AnyObject.Type
类型。
class CXTeacher {
var age = 18
}
var t: AnyClass = CXTeacher.self //这里 CXTeacher.self 就是属于 CXTeacher.Type 这个类型
这里 CXTeacher.self
就是属于 CXTeacher.Type
这个类型。
type(of:)
-
type(of:)
:动态获取传入参数的实际类型。
在 test
方法中,value
的静态类型是 Any
,但是它的真实类型是 Int
,可以通过 type(of:)
来动态获取,type(of:)
也可以理解为等同于 Self
。
Swift Runtime
class CXTeacher {
var age: Int = 18
func teach(){
print("teach")
}
}
let t = CXTeacher()
func test(){
var methodCount:UInt32 = 0
let methodlist = class_copyMethodList(CXTeacher.self, &methodCount)
for i in 0..
运行如上代码可以发现,当前不管是方法列表还是属性列表,此时都是空的。下面我们对属性跟方法加上 @objc
标识再试一次。
这里可以看到,加上 @objc
标识之后属性列表跟方法列表可以正常输出,不过这个时候 CXTeacher
类中的方法跟属性对 OC
来说是不能使用的,想让 OC
使用的话需要让 CXTeacher
继承于 NSObject
,不过继承于 NSObject
的类不加 @objc
的情况下,调用 test
方法属性列表跟方法列表也是为空。所以我们可以得出如下结论:
- 对于纯
Swift
类来说,方法和属性不加任何修饰符的情况下。这个时候其实已经不具备我们所谓的Runtime
特性了,这和我们在前面讲的方法调度(V-Table
调度)是不谋而合的。- 对于纯
Swift
类,方法和属性添加@obic
标识的情况下,当前我们可以通过Runtime API
拿到但是在我们的OC
中是没法进行调度的。- 对于继承自
NSObject
的类来说,如果我们想要动态的获取当前的属性和方法,必须在其声明前添加@objc
关键字,否则也是没有办法通过RuntimeAPI
获取的。- 纯
swift
类没有动态性,但在方法、属性前添加dynamic
修饰,可获得动态性。- 继承自
NSObiect
的swift
类,其继承自父类的方法具有动态性,其它自定义方法、属性想要获得动态性,需要添加dynamic
修饰。- 若方法的参数、属性类型为
swift
特有、无法映射到obiective-c
的类型如Character
、Tuple
,则此方法、属性无法添加dynamic
修饰(编译器报错)。
Mirror 的基本用法
所谓反射就是可以动态获取类型、成员信息,在运行时可以调用方法、属性等行为的特性。在使用 OC
开发时很少强调其反射概念,因为 OC
的 Runtime
要比其他语言中的反射强大的多。但是 Swift
是一门类型安全的语言,不支持我们像 OC
那样直接操作,但它的标准库仍然提供了反射机制来让我们访问成员信息,Swift
的反射机制是基于一个叫 Mirror
的结构体来实现的。你为具体的实例创建一个 Mirror
对象,然后就可以通过它查询这个实例。
- 用法介绍
class CXTeacher {
var age: Int = 18
}
//首先通过构造方法构建一个Mirror实例,这里传入的参参数是Any,也就意味着当前可以是类,结构体,枚举等
let mirror = Mirror(reflecting:CXTeacher())
//接下来遍历children属性,这是一个集合
for pro in mirror.children{
//然后我们可以直接通过label输出当前的名称 value 输出当前反射的值
print("\(String(describing: pro.label)):\(pro.value)")
}
实际使用案例
class CXTeacher {
var age: Int = 18
}
func test(_ mirro0bj:Any)-> Any{
let mirror=Mirror(reflecting: mirro0bj)
//这里做一下判断,如果当前子属性为空,那么当前返回的就是 mirro0bj 自身
guard !mirror.children.isEmpty else { return mirro0bj}
var result:[String:Any] = [:]
//这里是递归调用 test 方法,直到当前属性没有子属性了
for child in mirror.children {
if let key=child.label {
result[key] = test(child.value)
} else {
print("NO Keys")
}
}
return result
}
var result = test(CXTeacher())
print(result)
这里我们封装一个 test
方法,将所有的属性包装到字典里面并返回。但是如果我们想让结构体、类、枚举、基础类型都具备这个方法,这个时候我们就可以通过协议来实现,示例代码如下:
protocol JSONMap {
func jsonMap() -> Any
}
extension JSONMap {
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? JSONMap {
if let key = child.label {
result[key] = value.jsonMap()
} else {
print("NO Keys")
}
} else {
print("Value not conform JSONMap Protocol")
}
}
return result
}
}
class CXTeacher {
var age: Int = 18
}
extension CXTeacher: JSONMap {}
extension String: JSONMap {}
extension Int: JSONMap {}
print(CXTeacher().jsonMap())
在上面代码中,我们的错误都是通过 print
输出来代替的,这样的话会显得很不专业,这里我们通过 Swift
中的错误处理来合理表达一个错误。
Swift
提供 Error
协议来标识当前应用程序发生错误的情况,Error
的定义如下:
public protocol Error : Sendable {}
接下来我们把代码修改一下:
enum JSONMapError: Error {
case emptyKey
case notConformProtocol
}
protocol JSONMap {
func jsonMap() -> Any
}
extension JSONMap {
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? JSONMap {
if let key = child.label {
result[key] = value.jsonMap()
} else {
return JSONMapError.emptyKey
}
} else {
return JSONMapError.notConformProtocol
}
}
return result
}
}
这里在错误的时候我们 return JSONMapError.emptyKey
跟 return JSONMapError.notConformProtocol
,直接 return
的话这里跟平常没啥区别,但是如果让方法的调用者意识到有错误发生呢?正确的方式是使用 throws
关键字。修改后的代码如下:
enum JSONMapError: Error {
case emptyKey
case notConformProtocol
}
protocol JSONMap {
func jsonMap() throws -> Any
}
extension JSONMap {
func jsonMap() throws -> 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? JSONMap {
if let key = child.label {
result[key] = try? value.jsonMap()
} else {
return JSONMapError.emptyKey
}
} else {
return JSONMapError.notConformProtocol
}
}
return result
}
}
class CXTeacher {
var age: Int = 18
}
extension CXTeacher: JSONMap {}
extension String: JSONMap {}
extension Int: JSONMap {}
let t = CXTeacher()
// 如果有错误就会向上抛出,可以结合 do {} catch {} 使用
//try t.jsonMap()
// 使用 try? 的话,代表返回的是一个可选类型,如果成功就会返回一个字典,如果错误就返回一个nil,错误不会向上传递
//try? t.jsonMap()
// 使用 try! 的话代表 t.jsonMap 必须有返回结果,不会发生错误,否则的话就会抛出异常
//try! t.jsonMap()
do {
try t.jsonMap()
} catch {
print(error)
}
Mirror 源码解析
首先我们在源文件里面搜索 Mirror.Swift
, 在源码中我们可以很清晰的看到 Mirror
是由
结构体实现的,我们忽略掉一些细节,现在我们快速定位到初始化的方法。
public init(reflecting subject: Any) {
if case let customized as CustomReflectable = subject {
self = customized.customMirror
} else {
self = Mirror(internalReflecting: subject)
}
}
这里可以看到,初始化方法中接收一个 Any
类型的参数,同样的这里有一个 if case
的写法来判断当前的 subject
是否遵循了 customReflectable
协议,如果是,我们就直接调用 customMirror
,否则就进行下级函数的调用。
这里有两个需要注意的是 if case
的写法,这里其实是枚举 Case
的模式匹配,和我们的 Switch
一样,这里是只有一个 case
的 switch
语句。于此同时这里出现了一个 customRefletable
的协议。这里我们看一下 customRefletable
的具体用法:
首先我们遵循 customReflectable
协议,并实现其中的属性 customMirror
,customMirror
会返回一个 Mirror
对象。代码展示如下:
class CXTeacher: CustomReflectable {
var age: Int
var name: String
init(age: Int,name: String){
self.age = age
self.name = name
}
var customMirror: Mirror {
let info=KeyValuePairs.init(dictionaryLiteral:("age",age), ("name", name))
let mirror = Mirror.init(self, children: info, displayStyle: .class, ancestorRepresentation: .generated)
return mirror
}
}
当然实现这个 CustomReflectable
最直观的区别在与我们在 lldb debug
中会出现更详细的 debug
信息,下面我们看一下:
在上面 Mirror
的初始化方法中当 subject
不遵循 customReflectable
协议的时候就会执行 self = Mirror(internalReflecting: subject)
这句代码,所以我们全局搜索 Mirror(internalReflecting
,然后就可以定位到 ReflectionMirror .swift
文件。
如上图 137 行这句代码是用来获取当前 subject
的类型,当然这个函数最终调用的是 C++
的代码,这里使用了一个编译器字段 @ silgen_name
其实是 Swift
的一个隐藏符号,作用是将某个 C/C++
语言函数直接映射为 Swift
函数。也可以理解为为 C++
代码中的 swift_reflectionMirror_normalizedType
函数定义一个在 swift
中使用的别名 _getNormalizedType
,所以 _getNormalizedType
最终调用的是 ReflectionMirror.cpp
中的 C++
代码。
// 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; });
}
swift_reflectionMirror_normalizedType
函数往上找就能够找到 call
函数的实现,这里其实是一个回调函数,当前回调的具体数据都是由 ReflectionMirrorImpl
结构体实现。
auto call = [&](ReflectionMirrorImpl *impl) {
impl->type = type;
impl->value = value;
auto result = f(impl);
return result;
};
ReflectionMirrorImpl
结构体的具体实现(可以看到这是一个抽象类,也就意味着不同类型的
反射都需要去实现 ReflectionMirrorImpl
)这里我们在下面的代码中也能看到 class、struct、enum、Tuple
的具体实现。
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() {}
};
这里我们以 enum
为例来看一下 Mirror
都是如何获取到这些数据的。
struct EnumImpl : ReflectionMirrorImpl {
bool isReflectable() {
//将 EnumMetadata 进行类型强转
const auto *Enum = static_cast(type);
//找到 Enum 的描述信息 getDescription
const auto &Description = Enum->getDescription();
//找到 Description 中的 isReflectable 字段,标识是否可以接收反射
return Description->isReflectable();
}
const char *getInfo(unsigned *tagPtr = nullptr,
const Metadata **payloadTypePtr = nullptr,
bool *indirectPtr = nullptr) {
// 'tag' is in the range [0..NumElements-1].
unsigned tag = type->vw_getEnumTag(value);
StringRef name;
FieldType info;
std::tie(name, info) = getFieldAt(type, tag);
const Metadata *payloadType = info.getType();
bool indirect = info.isIndirect();
if (tagPtr)
*tagPtr = tag;
if (payloadTypePtr)
*payloadTypePtr = payloadType;
if (indirectPtr)
*indirectPtr = indirect;
return name.data();
}
char displayStyle() override {
return 'e';
}
// 这里 count 是获取属性数量
intptr_t count() override {
if (!isReflectable()) {
return 0;
}
// No fields if reflecting the enumeration type instead of a case
if (!value) {
return 0;
}
const Metadata *payloadType;
(nullptr, &payloadType, nullptr);
return (payloadType != nullptr) ? 1 : 0;
}
};
在 EnumImpl
中我们可以看到通过 count()
获取属性数量的时候会调用 getInfo
方法,在 getInfo
方法中获取属性名称跟信息的时候会调用 getFieldAt
方法,所以我们继续追踪 getFieldAt
方法。
可以看到是这里通篇都是通过 Metadata
、getDescription()
、FieldDescrition
这几个东西来去实现的,一个是当前类型的元数据、一个是当前类型的描述、一个是对当前类型属性的
描述。所以看到这里我们能够明白 Mirror
是如何工作的。