我有一个带有1个可选字段和1个非可选字段的类,它们都具有Type AnotherClass 类型,并且还符合 CustomProtocol :
protocol CustomProtocol {} class CustomClass: CustomProtocol { var nonoptionalField: AnotherClass = AnotherClass() var optionalField: AnotherClass? } class AnotherClass: CustomProtocol { }
现场 nonoptionalField 的类型 AnotherClass 和符合 CustomProtocol 。
另一方面, optionalField 实际上是 Optional ,因此不符合 CustomProtocol :
for field in Mirror(reflecting: CustomClass()).children { let fieldMirror = Mirror(reflecting: field.value) if fieldMirror.subjectType is CustomProtocol.Type { print("\(field.label!) is \(fieldMirror.subjectType) and conforms CustomProtocol") } else { print("\(field.label!) is \(fieldMirror.subjectType) and DOES NOT conform CustomProtocol") } } // nonoptionalField is AnotherClass and conforms CustomProtocol // optionalField is Optional<AnotherClass> and DOES NOT conform CustomProtocol
如何解开 optionalField 属性的Type(而不是值),以便可以将其与其协议 CustomProtocol 关联?
换句话说,如何从 Optional 类型中获取包装 好的 Type AnotherClass ? __
局限性:
我真的必须通过镜像使用Swift反射,不幸的是, 到目前为止,.subjectType 属性不允许解开 Optional 的可选包装类型。
我不认为有一种简单的方法来执行此操作,因为我们目前无法在没有占位符的情况下就泛型类型进行交谈-因此我们不能简单地转换为Optional.Type。
Optional.Type
我们Optional<Any>.Type也不能转换为,因为编译器无法为其提供实例的元类型值提供相同类型的自动转换(例如An Optional<Int>可以转换为an Optional<Any>,而an Optional<Int>.Type不能转换为a Optional<Any>.Type)。
Optional<Any>.Type
Optional<Int>
Optional<Any>
Optional<Int>.Type
然而,一种解决方案(尽管有些棘手)将是定义一个“虚拟协议”以表示“任何Optional实例”,而与Wrapped类型无关。然后,我们可以让该协议定义一个wrappedType要求,以便获得Wrapped给定Optional类型的元类型值。
Optional
Wrapped
wrappedType
例如:
protocol OptionalProtocol { // the metatype value for the wrapped type. static var wrappedType: Any.Type { get } } extension Optional : OptionalProtocol { static var wrappedType: Any.Type { return Wrapped.self } }
现在,如果fieldMirror.subjectType是Optional<Wrapped>.Type,我们可以将其转换为OptionalProtocol.Type,然后从中获取元wrappedType类型值。然后,这使我们可以检查CustomProtocol一致性。
fieldMirror.subjectType
Optional<Wrapped>.Type
OptionalProtocol.Type
CustomProtocol
for field in Mirror(reflecting: CustomClass()).children { let fieldMirror = Mirror(reflecting: field.value) // if fieldMirror.subjectType returns an optional metatype value // (i.e an Optional<Wrapped>.Type), we can cast to OptionalProtocol.Type, // and then get the Wrapped type, otherwise default to fieldMirror.subjectType let wrappedType = (fieldMirror.subjectType as? OptionalProtocol.Type)?.wrappedType ?? fieldMirror.subjectType // check for CustomProtocol conformance. if wrappedType is CustomProtocol.Type { print("\(field.label!) is \(fieldMirror.subjectType) and conforms CustomProtocol") } else { print("\(field.label!) is \(fieldMirror.subjectType) and DOES NOT conform CustomProtocol") } } // nonoptionalField is AnotherClass and conforms CustomProtocol // optionalField is Optional<AnotherClass> and conforms CustomProtocol
这仅处理单个级别的可选嵌套,但可以通过简单地反复尝试将结果元类型值强制转换为OptionalProtocol.Type并获取wrappedType,然后检查CustomProtocol一致性来轻松地应用于任意可选嵌套级别。
class CustomClass : CustomProtocol { var nonoptionalField: AnotherClass = AnotherClass() var optionalField: AnotherClass?? var str: String = "" } /// If `type` is an `Optional<T>` metatype, returns the metatype for `T` /// (repeating the unwrapping if `T` is an `Optional`), along with the number of /// times an unwrap was performed. Otherwise just `type` will be returned. func seeThroughOptionalType( _ type: Any.Type ) -> (wrappedType: Any.Type, layerCount: Int) { var type = type var layerCount = 0 while let optionalType = type as? OptionalProtocol.Type { type = optionalType.wrappedType layerCount += 1 } return (type, layerCount) } for field in Mirror(reflecting: CustomClass()).children { let fieldMirror = Mirror(reflecting: field.value) let (wrappedType, _) = seeThroughOptionalType(fieldMirror.subjectType) if wrappedType is CustomProtocol.Type { print("\(field.label!) is \(fieldMirror.subjectType) and conforms CustomProtocol") } else { print("\(field.label!) is \(fieldMirror.subjectType) and DOES NOT conform CustomProtocol") } } // nonoptionalField is AnotherClass and conforms CustomProtocol // optionalField is Optional<Optional<AnotherClass>> and conforms CustomProtocol // str is String and DOES NOT conform CustomProtocol