我正在参加「掘金·启航计划」
本文主要对Swift的反射实现有一个基本的认识,通过JSON解析来认识反射以及错误的实际使用。
主要内容
- Mirror的使用
- 错误Error的使用
1、 Mirror
Swift是一门静态语言,没有像OC中Runtime那样的运行时操作。但是Swift标准库中提供了反射机制,用来动态访问成员信息,这就是Mirror。(当然远没有Runtime那样强大)
1.1 基本使用
代码:
/*
1、使用
*/
class WYTeacher: NSObject {
var age: Int = 18
}
var t = WYTeacher()
let mirror = Mirror(reflecting: t.self)
for pro in mirror.children{
print("\(String(describing: pro.label)): \(pro.value)")
}
//Optional("age"): 18
说明:
- 传入的是一个对象
- 获取到MIrror对象,通过它就可以获取到t对象的成员,分别通过label、value获取属性和值
1.2 Mirror认识
Mirror的初始化:
public init(reflecting subject: Any)
说明:
- 进入Mirror初始化方法,发现传入的类型是Any,
- 正是这个原因,我们才可以直接传t
- 创建在给定实例上进行反射的反射对象
- 如果subject的类型遵守CustomReflectable协议,生成的mirror由customMirror属性决定
children
public let children: Mirror.Children
👇
//进入Children,发现是一个AnyCollection,接收一个泛型
public typealias Children = AnyCollection<Mirror.Child>
👇
//进入Child,发现是一个元组类型,由可选的标签和值构成,
public typealias Child = (label: String?, value: Any)
说明:
- children的类型为Children
- 而Children是AnyCollection<Mirror.Child>的别名,是所有Child的集合
- Child又是一个元组,由label、value组成
- 分别用来存储属性和值
2、JOSN的实现
通过反射可以很轻松的获取到一个对象的成员,因此我们可以用来进行JSON解析,将一个对象转换成JSON格式
代码:
/*
2、JSON解析
*/
//Int、String遵守协议
extension Int: CustomJSONMap{}
extension String: CustomJSONMap{}
//1、定义一个JSON解析协议
protocol CustomJSONMap {
func jsonMap() -> Any
}
//2、提供一个默认实现
extension CustomJSONMap{
func jsonMap() -> Any{
let mirror = Mirror(reflecting: self)
//递归终止条件
guard !mirror.children.isEmpty else {
return self
}
//字典,用于存储json数据
var keyValue: [String: Any] = [:]
//遍历
for children in mirror.children {
if let value = children.value as? CustomJSONMap {
if let keyName = children.label {
//递归
keyValue[keyName] = value.jsonMap()
}else{
print("key是nil")
}
}else{
print("当前-\(children.value)-没有遵守协议")
}
}
return keyValue
}
}
//3、让类遵守协议(注意:类中属性的类型也需要遵守协议,否则无法解析)
class WYTeacher: CustomJSONMap {
var age = 18
var name = "WY"
}
//使用
var t = WYTeacher()
print(t.jsonMap())
说明:
- 专门定义一个用来进行JSON解析的协议,这样可以给你任意类型的对象进行解析,只需要这个类型遵守该协议即可
- 这里的实现也就是拿到对象的成员,分别存储到字典中,之后返回
- 这里进行了递归调用,因为有可能属性本身也是一个对象。以一个字典的形式再存储到这个key的value上
- 类中属性的类型也需要遵守协议,因为这里涉及到递归调用
3、Error的使用
在上文对于错误的处理是仅仅进行打印,这样并不规范,Swift中也提供了Error的处理方式。
Error的认识可以看我的另一篇博客Swift语法(十)Error
3.1 Error
Swift中,提供了Error协议来标识当前应用程序发生错误的情况。struct、Class、enum都可以遵守该协议,自定义错误。
代码:
//定义错误类型
enum JSONMapError: Error{
case emptyKey
case notConformProtocol
}
protocol CustomJSONMap {
func jsonMap() throws -> Any
}
//2、提供一个默认实现
extension CustomJSONMap{
func jsonMap() throws -> Any{
let mirror = Mirror(reflecting: self)
//递归终止条件
guard !mirror.children.isEmpty else {
return self
}
//字典,用于存储json数据
var keyValue: [String: Any] = [:]
//遍历
for children in mirror.children {
if let value = children.value as? CustomJSONMap {
if let keyName = children.label {
//递归
keyValue[keyName] = try value.jsonMap()
}else{
throw JSONMapError.emptyKey
}
}else{
throw JSONMapError.notConformProtocol
}
}
return keyValue
}
}
var t = WYTeacher()
print(try t.jsonMap())
说明:
- 定义一个Error的枚举
- 可以使用throw抛出异常
- 方法中通过throws将错误抛出
- 可能抛出异常的函数需要使用try调用,避免编译报错
- 但是仅仅使用try调用,没有进行处理,这样会闪退
- 可以使用try ... catch进行错误处理。具体的处理可以看另一篇博客,这里不再赘述
3.2 LocalError协议
如果只是用Error还不足以表达更详尽的错误信息,可以使用LocalizedError协议,其定义如下
定义:
public protocol LocalizedError : Error {
/// A localized message describing what error occurred.错误描述
var errorDescription: String? { get }
/// A localized message describing the reason for the failure.失败原因
var failureReason: String? { get }
/// A localized message describing how one might recover from the failure.建议
var recoverySuggestion: String? { get }
/// A localized message providing "help" text if the user requests help.帮助
var helpAnchor: String? { get }
}
说明:
- LocalError协议提供了多种属性
- 我们可以给协议的这些属性增加描述,这样在错误处理时就可以进行打印
使用: 此时不给Int和String遵守CustomJSONMap,因此会走.notConformProtocol的分支
//定义更详细的错误信息
extension JSONMapError: LocalizedError{
var errorDescription: String?{
switch self {
case .emptyKey:
return "key为空"
case .notConformProtocol:
return "没有遵守协议"
}
}
}
var t = WYTeacher()
do{
try t.jsonMap()
}catch{
print(error.localizedDescription)
}
运行结果:
3.3 CustomNSError协议
CustomNSError相当于OC中的NSError,其定义如下,有三个默认属性
定义:
public protocol CustomNSError : Error {
/// The domain of the error.
static var errorDomain: String { get }
/// The error code within the given domain.
var errorCode: Int { get }
/// The user-info dictionary.
var errorUserInfo: [String : Any] { get }
}
使用:
//CustomNSError相当于NSError
extension JSONMapError: CustomNSError{
var errorCode: Int{
switch self {
case .emptyKey:
return 404
case .notConformProtocol:
return 504
}
}
}
var t = WYTeacher()
do{
try t.jsonMap()
}catch{
print((error as? JSONMapError)?.errorCode)
}
运行结果:
4、总结
- Swift使用Mirror实现反射功能,可以动态获取对象的属性
- 通过Mirror可以实现JSON的解析
- Error是swift中错误类型的基本协议
- 其中LocalizedError、CustomNSError都遵守Error,他们可以提供更详细的信息
- try调用可能发生错误信息的函数