可选字段类型不符合 Swift 3 中的协议 [英] Optional field type doesn't conform protocol in Swift 3

查看:35
本文介绍了可选字段类型不符合 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.Type,因为编译器没有为它为实例提供的元类型值提供相同类型的自动转换(例如 An Optional<;Int> 可转换为 Optional,但 Optional.Type 不能转换为 Optional.输入).

然而,一种解决方案,尽管有点老套,是定义一个虚拟协议"来表示任何 Optional 实例",而不管 Wrapped 类型如何.然后我们可以让这个协议定义一个 wrappedType 要求,以获得给定 Optional 类型的 Wrapped 元类型值.

例如:

protocol OptionalProtocol {//包装类型的元类型值.静态 var 包装类型:Any.Type { 获取}}扩展可选:OptionalProtocol {静态 var 包装类型:Any.Type { return Wrapped.self }}

现在如果 fieldMirror.subjectTypeOptional.Type,我们可以将它转换为 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆