可选字段类型不符合 Swift 3 中的协议 [英] Optional field type doesn't conform protocol in Swift 3
问题描述
我有一个包含 1 个可选字段和 1 个非可选字段的类,它们都具有类型 AnotherClass 并且也符合 CustomProtocol:
protocol CustomProtocol {}类自定义类:自定义协议{var nonoptionalField: AnotherClass = AnotherClass()var optionalField:AnotherClass?}类另一个类:自定义协议{}
字段 nonoptionalField 的类型为 AnotherClass 并且符合 CustomProtocol.>
另一方面,optionalField 实际上是Optional<另一个类>,因此不符合CustomProtocol:
for field in Mirror(reflecting: CustomClass()).children {让 fieldMirror = Mirror(反射:field.value)如果 fieldMirror.subjectType 是 CustomProtocol.Type {print("\(field.label!) 是 \(fieldMirror.subjectType) 并且符合 CustomProtocol")} 别的 {print("\(field.label!) 是 \(fieldMirror.subjectType) 并且不符合 CustomProtocol")}}//nonoptionalField 是 AnotherClass 并且符合 CustomProtocol//optionalField 是可选的并且不符合 CustomProtocol
如何解开 optionalField 属性的类型(而不是值),以便我可以将其与其协议 CustomProtocol 相关联?
换句话说,我怎样才能从 Optional< 获得包装好的类型 AnotherClass另一个类>类型?
限制:
我真的必须通过 Mirror 使用 Swift 反射,不幸的是,.subjectType 属性不允许解开 Optional< 的可选包装类型.到目前为止,AnotherClass>.
我不相信有一种简单的方法可以做到这一点,因为我们目前无法在没有占位符的情况下谈论泛型类型——因此我们不能简单地强制转换为Optional.Type
.
我们也不能强制转换为 Optional
,因为编译器没有为它为实例提供的元类型值提供相同类型的自动转换(例如 An Optional<;Int>
可转换为 Optional
,但 Optional
不能转换为 Optional
.输入).
然而,一种解决方案,尽管有点老套,是定义一个虚拟协议"来表示任何 Optional
实例",而不管 Wrapped
类型如何.然后我们可以让这个协议定义一个 wrappedType
要求,以获得给定 Optional
类型的 Wrapped
元类型值.
例如:
protocol OptionalProtocol {//包装类型的元类型值.静态 var 包装类型:Any.Type { 获取}}扩展可选:OptionalProtocol {静态 var 包装类型:Any.Type { return Wrapped.self }}
现在如果 fieldMirror.subjectType
是 Optional
,我们可以将它转换为 OptionalProtocol.Type
,然后从那里开始获取 wrappedType
元类型值.然后让我们检查 CustomProtocol
的一致性.
for field in Mirror(reflecting: CustomClass()).children {让 fieldMirror = Mirror(反射:field.value)//如果 fieldMirror.subjectType 返回一个可选的元类型值//(即Optional<Wrapped>.Type),我们可以转换为OptionalProtocol.Type,//然后获取 Wrapped 类型,否则默认为 fieldMirror.subjectType让wrappedType = (fieldMirror.subjectType as?OptionalProtocol.Type)?.wrappedType??fieldMirror.subjectType//检查 CustomProtocol 的一致性.如果wrappedType 是CustomProtocol.Type {print("\(field.label!) 是 \(fieldMirror.subjectType) 并且符合 CustomProtocol")} 别的 {print("\(field.label!) 是 \(fieldMirror.subjectType) 并且不符合 CustomProtocol")}}//nonoptionalField 是 AnotherClass 并且符合 CustomProtocol//optionalField 是可选的并符合 CustomProtocol
这仅处理单个级别的可选嵌套,但可以通过简单地重复尝试将结果元类型值强制转换为 OptionalProtocol.Type
并获取wrappedType
,然后检查 CustomProtocol
的一致性.
class CustomClass : CustomProtocol {var nonoptionalField: AnotherClass = AnotherClass()var optionalField: AnotherClass??var str: 字符串 = ""}///如果 `type` 是 `Optional<T>` 元类型,则返回 `T` 的元类型///(如果`T` 是`Optional`,则重复展开),以及///执行解包的次数.否则只会返回 `type`.func seeThroughOptionalType(_ 类型:Any.Type) ->(wrappedType: Any.Type, layerCount: Int) {var 类型 = 类型无功层数 = 0而 let optionalType = type as?OptionalProtocol.Type {type = optionalType.wrappedType层数 += 1}返回(类型,层数)}对于 Mirror 中的字段(反射:CustomClass()).children {让 fieldMirror = Mirror(反射:field.value)让 (wrappedType, _) = seeThroughOptionalType(fieldMirror.subjectType)如果wrappedType 是CustomProtocol.Type {print("\(field.label!) 是 \(fieldMirror.subjectType) 并且符合 CustomProtocol")} 别的 {print("\(field.label!) 是 \(fieldMirror.subjectType) 并且不符合 CustomProtocol")}}//nonoptionalField 是 AnotherClass 并且符合 CustomProtocol//optionalField 是 Optional>并符合 CustomProtocol//str 是字符串并且不符合 CustomProtocol
I have a class with 1 optional field and 1 non-optional field, both of them with Type AnotherClass and also conform CustomProtocol:
protocol CustomProtocol {}
class CustomClass: CustomProtocol {
var nonoptionalField: AnotherClass = AnotherClass()
var optionalField: AnotherClass?
}
class AnotherClass: CustomProtocol {
}
The field nonoptionalField is type AnotherClass and conforms CustomProtocol.
On the other hand, optionalField is actually Optional< AnotherClass> and therefore DOES NOT conform 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
How can I unwrap the Type (not the value) of optionalField property, so that I can associate it with its protocol CustomProtocol?
In other words, how can I get the wrapped Type AnotherClass from Optional< AnotherClass> Type?
LIMITATION:
I really have to use Swift reflection through Mirror and unfortunately the property .subjectType doesn't allow to unwrap the optional wrapped Type of Optional< AnotherClass> so far.
I do not believe there's a simple way to do this, given that we currently cannot talk in terms of generic types without their placeholders – therefore we cannot simply cast to Optional.Type
.
Nor can we cast to Optional<Any>.Type
, because the compiler doesn't provide the same kinds of automatic conversions for metatype values that it provides for instances (e.g An Optional<Int>
is convertible to an Optional<Any>
, but an Optional<Int>.Type
is not convertible to a Optional<Any>.Type
).
However one solution, albeit a somewhat hacky one, would be to define a 'dummy protocol' to represent an 'any Optional
instance', regardless of the Wrapped
type. We can then have this protocol define a wrappedType
requirement in order to get the Wrapped
metatype value for the given Optional
type.
For example:
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 }
}
Now if fieldMirror.subjectType
is an Optional<Wrapped>.Type
, we can cast it to OptionalProtocol.Type
, and from there get the wrappedType
metatype value. This then lets us check for CustomProtocol
conformance.
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
This only deals with a single level of optional nesting, but could easily be adapted to apply to an arbitrary optional nesting level through simply repeatedly attempting to cast the resultant metatype value to OptionalProtocol.Type
and getting the wrappedType
, and then checking for CustomProtocol
conformance.
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
这篇关于可选字段类型不符合 Swift 3 中的协议的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!