有一个这样的日志需求,记录异常的网络请求信息,以便排查问题。通过model用来传递,和其它信息汇总到一个字典中写入本地等待上传。
@objc public class BTLogNetworkParams: NSObject {
/// api排查的path,包含http及socket
@objc public var networkApiPath: String?
/// 参数内容
public var networkParams: [String: Any]?
/// 接口状态
@objc public var networkStatus: Int = 0
/// 响应时间
@objc public var networkRequestTime: Int32 = 0
/// status != 1的返回值。
public var networkResponse: [String: Any]?
}
模型中属性转字典,自然的就使用了反射的方式Mirror。
public protocol BDLogParamTransfersable {
/// 转换为字典
func transferToDictionary() -> [String: Any]
}
extension BDLogParamTransfersable {
public func transferToDictionary() -> [String: Any] {
let hMirror = Mirror(reflecting: self)
var transferDic = [String:Any]()
for case let children in hMirror.children {
let label = children.label
let value = children.value
if let la = label {
transferDic.updateValue(value, forKey: la)
}
}
return transferDic
}
}
let tempparams = BTLogNetworkParams()
tempparams.networkParams = ["a": "1", "b": "2"]
let tempDcit = tempparams.transferToDictionary()
print(tempDcit)
// ["networkResponse": nil, "networkRequestTime": 0, "networkStatus": 0, "networkParams": Optional(["a": "1", "b": "2"]), "networkApiPath": nil]
诉求:想把为nil的过滤掉。
准备
struct MyStruct {
let myString: String?
let myInt: Int?
let myDouble: Double?
init(_ myString: String?, _ myInt: Int?, _ myDouble: Double?) {
self.myString = myString
self.myInt = myInt
self.myDouble = myDouble
}
}
创建一个这样的结构体备用。
PartOne 判断是否为nil
Mirror 是一个结构体, 有一个children的属性,children是一个集合,元素是Child类型。
public struct Mirror { public typealias Child = (label: String?, value: Any) public typealias Children = AnyCollection<Mirror.Child> }
let myStruct = MyStruct("合合信息", nil, nil)
let children = Mirror(reflecting: myStruct).children
let properties = children.filter {
$0.label != nil
}
print(properties)
// [(label: Optional("myString"), value: Optional("合合信息")), (label: Optional("myInt"), value: nil), (label: Optional("myDouble"), value: nil)]
for property in properties {
/// Comparing non-optional value of type 'Any' to 'nil' always returns false
if property.value == nil {
print("property (property.label ?? "") is nil")
}
}
提示警告: Comparing non-optional value of type 'Any' to 'nil' always returns false
发现 children.value 是一个Any类型, 怎么可能会为nil呢?
children.value 是明确的Any类型, 打印 又是一个nil,可以类型转换为String,来判断。
for property in properties {
if checkAnyContainsNil(object: property.value) {
print("property (property.label ?? "") is nil")
}
}
func checkAnyContainsNil(object : Any) -> Bool {
let value = "(object)"
if value == "nil" {
return true
}
return false
}
PartTwo 是否可以将Any类型的值用Any?来承接,再判断是否为nil?
let myStruct = MyStruct("合合信息", nil, nil)
let children = Mirror(reflecting: myStruct).children
let properties = children.filter {
$0.label != nil
}
for property in properties {
let value = property.value
let tempValue: Any? = value
print(tempValue)
/** 输出
Optional(Optional("合合信息"))
Optional(nil)
Optional(nil)
*/
// 不能将Any强制转换为Any?类型
// Any不能与nil进行比较,因为它实际上持有包含nil的枚举Optional< T >。
if tempValue == nil {
print("property (property.label ?? "") is nil")
}
//⚠️ value 有可能是非String类型,不可以这么处理。
if !(value is String) {
print("property (property.label ?? "") is nil")
// property myInt is nil
// property myDouble is nil
}
}
- 不能将Any强制转换为Any?类型
- Any不能与nil进行比较,因为它实际上持有包含nil的枚举Optional< T >
- 特定情况下,!(value is String) 可以这么判断达到目的。【 不合理的处理方案:有可能是非String类型】
PartThree 利用可选类型来解决问题
Optional 是一个枚举,none 和 some(Any) 两个case。
@frozen public enum Optional<Wrapped> : ExpressibleByNilLiteral { /// The absence of a value. /// /// In code, the absence of a value is typically written using the `nil` /// literal rather than the explicit `.none` enumeration case. case none /// The presence of a value, stored as `Wrapped`. case some(Wrapped) }刚才说到: Any不能与nil进行比较,因为它实际上持有包含nil的枚举Optional。
let myStruct = MyStruct("合合信息", nil, nil)
let children = Mirror(reflecting: myStruct).children
// 为nil的情况
for case (let label, Optional<Any>.none) in children {
// property myInt is nil
// property myDouble is nil
print("property (label ?? "") is nil")
}
/// 为nil 和 不为nil的条件
for property in children {
if let label = property.label {
if case Optional<Any>.some(let x) = property.value {
print("property (label) is not nil (value: (x))")
}
else {
print("property (label) is nil")
}
}
}
/// 为nil 和 不为nil的条件
for property in children {
switch(property) {
case (let label, Optional<Any>.some(let x)):
print("property (label ?? "") is not nil (value: (x))")
case (let label, _):
print("property (label ?? "") is nil")
}
}
PartFour Optional
/// 实现一个Optional
let company: Optional<String> = "合合信息"
let product: String? = "启信宝"
print(company)
print(product)
// Optional("合合信息")
// Optional("启信宝")
let name: String? = "小明"
let wrapped = name.unsafelyUnwrapped
print(wrapped)
print(wrapped.count)
// 小明
// 2
let year: String?
// Constant 'year' used before being initialized
print(year.unsafelyUnwrapped)
var area: String?
print(area.unsafelyUnwrapped) // ⚠️ Crash: unsafelyUnwrapped of nil optional
声明一个可选值
- Optional
- String?
两种效果一样。
unsafelyUnwrapped
获取非nil case的some值,如果没有值就会crash。
谨慎使用 unsafelyUnwrapped
- let声明的常量,如果没有被initialized,编译期间就会报错。
- var声明的变量,如果没有被initialized,运行期间会crash。unsafelyUnwrapped of nil optional
\