Swift
是静态语言
,他不能像OC一样,直接获取对象的属性和方法,但是Swift
标准库依旧提供了反射机制
,用来访问成员信息,即Mirror
。
一、Mirror反射
反射:是指可以动态获取类型、成员信息,在运行时可以调用方法、属性等行为的特性。
使用Mirror
的初始化方法reflecting
,接着通过.children
读取属性名与值
。代码:
class Animal {
var age: Int = 18
var name: String = "Cat"
}
let mirror = Mirror(reflecting: Animal().self)
for pro in mirror.children{
print("\(pro.label ?? ""): \(pro.value)")
}
//打印结果:
//age: 18
//name: Cat
1.1 Mirror源码
进入Mirror
初始化方法,发现传入的类型是Any
,则可以直接传t
:
// Creates a mirror that reflects on the given instance.
// - Parameter subject: The instance for which to create a mirror.
public init(reflecting subject: Any)
查看children
:
/// A collection of `Child` elements describing the structure of the reflected subject.
public let children: Mirror.Children
//进入Children,发现是一个AnyCollection,接收一个泛型
public typealias Children = AnyCollection
//进入Child,发现是一个元组类型,由可选的标签和值构成,
public typealias Child = (label: String?, value: Any)
联系示例代码,通过print("\(pro.label ?? ""): \(pro.value)")
打印的就是key
与value
。
1.2 JSON解析
我们定义了一个Animal
类,然后通过一个test
方法来解析:
class Animal {
var age = 18
var name = "Cat"
}
//JSON解析
func test(_ obj: Any) -> Any{
let mirror = Mirror(reflecting: obj)
//判断条件 - 递归终止条件
guard !mirror.children.isEmpty else {
return obj
}
//字典
var keyValue: [String: Any] = [:]
//遍历
for children in mirror.children {
if let keyName = children.label {
//递归调用
keyValue[keyName] = test(children.value)
}else{
print("children.label 为空")
}
}
return keyValue
}
var kv: [String: Any] = test(Animal()) as! [String : Any]
print(kv)
//打印结果:
//["name": "Cat", "age": 18]
为了方便扩展、通用
,我们可以利用封装
的思想,将其抽象为一个协议
,然后提供一个默认实现,让类遵守协议,进而,model就具备了JSON解析
的功能。
//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 Animal: CustomJSONMap {
var age = 18
var name = "Cat"
}
//使用
var t = Animal()
print(t.jsonMap())
//打印结果:
//当前-18-没有遵守协议
//当前-Cat-没有遵守协议
//[:]
没有成功,提示,属性没有遵守协议。添加代码:
extension Int: CustomJSONMap{}
extension String: CustomJSONMap{}
extension Double: CustomJSONMap{}
//打印结果:
//["age": 18, "name": "Cat"]
HandyJson
二、Error
为了JSON
处理更规范,更好的维护和管理,我们需要对错误更规范化的处理。Swift
,提供了Error协议
来标识当前应用程序发生错误的情况。其中Error
的定义如下:
//A type representing an error value that can be thrown.
public protocol Error { }
如上,Error
是一个空协议
,其中没有任何实现,这也就意味着你可以遵守该协议,然后自定义错误类型
。所以,struct
、Class
、enum
等,都可以遵循这个Error
来表示一个错误。
下面,我们结合JSON解析
,做一下错误处理:
//定义错误类型
enum JSONMapError: Error{
case emptyKey
case notConformProtocol
}
//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{
return JSONMapError.emptyKey
}
}else{
return JSONMapError.notConformProtocol
}
}
return keyValue
}
}
这里有一个问题,jsonMap
方法的返回值是Any
,我们无法区分返回的是错误还是json
数据,那么该如何区分,即如何抛出错误呢?在这里可以通过throw
关键字,将Demo中原本return的错误,改成throw抛出
。
//1、定义一个JSON解析协议
protocol CustomJSONMap {
func jsonMap() throws-> Any
}
//2、提供一个默认实现
//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
}
}
因为我们使用了throw
,所以还需要在递归调用前增加 try
关键字。
//使用
var t = Animal()
print(try t.jsonMap())
三、错误处理方式
Swift
处理错误的方式,主要有两种,try - throw
,do - catch
。
3.1 try - throw
使用try关键字
,是最简便的,将Error
向上抛出,抛给上层函数。但是在使用时,需要注意以下两点:
-
try?
:返回一个可选类型
,成功,返回具体的字典值;错误,但并不关心是哪种错误,统一返回nil
。 -
try!
:表示你对这段代码有绝对的自信,这行代码绝对不会发生错误。
//CJLTeacher中定义一个height属性,并未遵守协议
class Animal: CustomJSONMap {
var age = 18
var name = "CJL"
var height = 1.85
}
let t = Animal()
//1、try?
print(try? t.jsonMap())
//打印结果:nil
//2、try!
print(try! t.jsonMap())
//打印结果:
//SwiftTest/main.swift:256: Fatal error: 'try!' expression unexpectedly raised an error: SwiftTest.JSONMapError.notConformProtocol
//2022-08-29 23:14:16.107880+0800 SwiftTest[10620:17556994] SwiftTest/main.swift:256: Fatal error: 'try!' expression unexpectedly raised an error: SwiftTest.JSONMapError.notConformProtocol
//3、直接使用try,是向上抛出-
try t.jsonMap()
//打印结果:
//Swift/ErrorType.swift:200: Fatal error: Error raised at top level: SwiftTest.JSONMapError.notConformProtocol
//2022-08-29 23:14:47.827688+0800 SwiftTest[10648:17557924] Swift/ErrorType.swift:200: Fatal error: Error raised at top level: SwiftTest.JSONMapError.notConformProtocol
从上面可以知道,try
错误是向上抛出的,即抛给了上层函数,如果上层函数也不处理,则直接抛给main,main没有办法处理,则直接报错
。
3.2 do - catch
其中do
处理正确结果
,catch
处理error
,catch
有个隐藏参数,就是error(Error类型)
do {
// 处理正常结果
try t.jsonMap() // 这里try不会报错了,因为错误都分给了catch
} catch {
// 处理错误类型(其中error为Error类型)
print(error)
}
//打印结果:notConformProtocol
3.3 LocalizedError
以上代码,只打印了错误类型
,如果还想知道错误相关的描述或其它信息
,则需要使用LocalizedError
,定义:
/// Describes an error that provides localized messages describing why
/// an error occurred and provides more information about the error.
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 }
}
接下来,我们来使用这个协议
,继续修改上面JSON的解析代码,新增代码如下:
//定义更详细的错误信息
extension JSONMapError: LocalizedError{
var errorDescription: String?{
switch self {
case .emptyKey:
return "key为空"
case .notConformProtocol:
return "没有遵守协议"
}
}
}
do {
// 处理正常结果
try t.jsonMap() // 这里try不会报错了,因为错误都分给了catch
} catch {
// 处理错误类型(其中error为Error类型)
print(error.localizedDescription)
}
//打印结果:没有遵守协议
3.3 CustomNSError
CustomNSError
协议,相当于OC
中的NSError
,其定义如下,有三个默认属性:
/// Describes an error type that specifically provides a domain, code,
/// and user-info dictionary.
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 }
}
继续修改JSON解析中的JSONMapError
,让其遵守CustomNSError协议
,如下:
//CustomNSError相当于NSError
extension JSONMapError: CustomNSError{
var errorCode: Int{
switch self {
case .emptyKey:
return 404
case .notConformProtocol:
return 504
}
}
}
do {
// 处理正常结果
try t.jsonMap() // 这里try不会报错了,因为错误都分给了catch
} catch {
//1、 处理错误类型(其中error为Error类型)
print(error.localizedDescription)
//2、处理错误类型(其中error为Error类型
print((error as? CustomNSError)?.errorCode as Any)
}
//catch1 打印结果:没有遵守协议
//catch2 打印结果:Optional(504)
rethorws
rethrows
是处理这种场景的错误:函数1是函数2的入参,其中函数1中有throws错误
。
代码:
func add(_ a: Int, _ b: Int) throws -> Int {
return a + b
}
func exec(_ f:(Int, Int) throws -> Int, _ a: Int, _ b: Int) rethrows {
print(try f(a, b) )
}
do {
try exec(add, 1, 2)
}catch {
print(error.localizedDescription)
}
defer(延后处理)
func functionDefer() {
print("begin")
defer {
print("defer1")
}
defer {
print("defer2")
}
print("end")
}
functionDefer()
//打印结果: begin end defer2 defer1
函数执行完成之前
,才会执行defer
代码块内部的逻辑。如果有多个defer
代码块,defer代码块的执行顺序是逆序
的。