将Any转换为Optional

时间:2015-12-30 16:49:48

标签: swift generics casting

我正在处理一组表示实体及其属性的类,然后可以动态地从实体创建编辑器表视图。这些属性使用泛型来捕获属性类型。为了使用KVO并生成自动设置器,这些属性包含键路径。这是属性类的一个非常简化的版本:

class XUEntityProperty<Entity: NSManagedObject, Value> {
    let keyPath: String
    var customSetter: ((Entity, Value) -> Void)?

    func setValue(value: Value, onEntity entity: Entity) {
        /// If custom setter is set, use it.
        if let setter = self.customSetter {
            setter(entity, value)
            return
        }

        /// Otherwise set the object using the keypath.
        guard let objValue = value as? AnyObject else {
            XUThrowAbstractException() // Use custom setter
        }

        entity.setValue(objValue, forKeyPath: self.keyPath)
    }
}

这对几乎任何事情都很有效。问题在于选择权问题。 E.g:

let property = XUEntityProperty<MyEntity, NSDate?>(keyPath: "optionalDate")

此处的问题是,在setValue方法中,转化为AnyObject会失败,因为值为Optional<NSDate>,无法转换为AnyObject - {{即使objValue as? NSDatenil,也会返回objValue

我正在寻找一种解决方法,通过检测和解包.Some(_)来解决自动设置器中的问题。

我尝试的任何演员都会导致编译器抱怨转换为更可选的类型,或者最终将Optional包含在另一个Optional内。

有没有人知道如何检测Optional 的值是否属于Any ,如果是,提取值来自可选项,将其转换为Optional

举个例子在操场上试试:

AnyObject

2 个答案:

答案 0 :(得分:5)

您可以通过添加伪协议并使用is进行类型检查来检查可选类型,并在Mirror(..)之后用于从可选Any中提取实际类型值:

protocol IsOptional {}
extension Optional : IsOptional {}

/* Detect if any is of type optional */
let any: Any = Optional<String>("123")
var object : AnyObject? = nil
switch any {
case is IsOptional:
    print("is an optional")
    if let (_, a) = Mirror(reflecting: any).children.first {
        object = a as? AnyObject
    }
default:
    print("is not an optional")
} /* Prints "is an optional" */

/* Detect if any2 is of type optional */
let any2: Any = String("123")
switch any2 {
case is IsOptional:
    print("is an optional")
    // ...
default:
    print("is not an optional")
} /* Prints "is not an optional" */

Charlie Monroes自己的最终解决方案非常整洁(+1!)。我想我会添加一个补充。

鉴于已定义协议_XUOptional并且Optional类型已扩展(如Charlies的答案),您可以在any是可选的时候处理事件line,使用可选链接和nil合并运算符:

let anyOpt: Any = Optional<String>("123")
let anyNotOpt: Any = String("123")
var object: AnyObject?

object = (anyOpt as? _XUOptional)?.objectValue ?? (anyOpt as? AnyObject)
/* anyOpt is Optional(..) and left clause of nil coalescing operator
   returns the unwrapped .objectValue: "123" as 'AnyObject'           */

object = (anyNotOpt as? _XUOptional)?.objectValue ?? (anyNotOpt as? AnyObject)
/* anyNotOpt is not optional and left-most optional chaining of left
   clause returns nil ('anyNotOpt as? _XUOptional' -> nil).
   In that case, right clause will successfully cast the non-optional
   'Any' type to 'AnyObject' (with value "123")                       */

答案 1 :(得分:3)

所以,这就是解决方案。需要一些解决方法,但可以根据需要工作。感谢@dfri关于协议的想法!

private protocol _XUOptional {

    var objectValue: AnyObject? { get }

}

extension Optional: _XUOptional {

    /// This property will return nil if the value is nil,
    /// or if the value isn't castable to AnyObject. String,
    /// Int, Bool and a few others are automatically converted
    /// to their ObjC counterparts.
    var objectValue: AnyObject? {
        switch self {
        case .None:
            return nil
        case .Some(_):
            return self! as? AnyObject
        }
    }

}

let any: Any = Optional<String>("123")
let obj = (any as! _XUOptional).objectValue!
obj.dynamicType /// _NSContiguousString.Type
相关问题