Swift:如何检查泛型类型的对象是否是类型子类型的数组

时间:2016-07-21 07:59:39

标签: swift generics realm alamofire objectmapper

我正在开发一个涉及Alamofire,ObjectMapper和RealmSwift的Swift项目,遇到一种情况,我必须检查泛型类型的对象是否是Realm Object的数组。

我的问题是:如何检查泛型类型的对象是否是Swift中某个类型子类型的数组?

冗长的版本:我制作了一个我的问题的最小工作示例。请参阅我的gist here。 您可以将其复制到Xcode Playground,并看到第31行运行而第35行没有。

我能够通过更改70&线来使其工作。 71

if let array = data as? [Object] {
  add(array)
}

if let array = data as? NSArray where array.count > 0 && array[0] is Object {
  add(array as! [Object])
}

但是这个解决方案甚至还不完美,因为它必须涉及Foundation。我更喜欢“纯粹的”斯威夫特。

<script src="https://gist.github.com/T-Pham/44fe5b7c3a669db34d856b54e15f278a.js"></script>

已移除:短版本无法完全代表实际问题。

简短版本:

protocol Protocol {
  init()
}

class Parent {}
final class Child1: Parent, Protocol {}
final class Child2: Parent, Protocol {}

func foo<T: Protocol>(array: [T]) {
  if array is [Parent] { // This won't compile
  }
}

foo([Child1(), Child1()])

1 个答案:

答案 0 :(得分:0)

在Swift类型系统中,[Child][Parent]并不相同,因为Swift的泛型并不支持协方差。您可以使用泛型类型约束重载,而不是子类型多态和动态类型转换。

首先,创建与handleResponse()方法相同的myRequest〜方法的三个重载:

func handleResponse(alamofireReponse: AlamofireReponse<AnyObject, NSError>, completionHandler: (MyResponse<AnyObject> -> Void)) {
    switch alamofireReponse {
    case .Success(let data):
        completionHandler(.Success(data))
    case .Failure(let error):
        completionHandler(.Failure(error))
    }
}

func handleResponse<T: Mappable where T: Object>(alamofireReponse: AlamofireReponse<T, NSError>, completionHandler: (MyResponse<T> -> Void)) {
    switch alamofireReponse {
    case .Success(let data):
        add(data)
        completionHandler(.Success(data))
    case .Failure(let error):
        completionHandler(.Failure(error))
    }
}

func handleResponse<T: Mappable where T: Object>(alamofireReponse: AlamofireReponse<[T], NSError>, completionHandler: (MyResponse<[T]> -> Void)) {
    switch alamofireReponse {
    case .Success(let data):
        add(data)
        completionHandler(.Success(data))
    case .Failure(let error):
        completionHandler(.Failure(error))
    }
}

然后,更改myRequest〜方法的类型约束以满足每个handleResponse方法的约束。

func myRequestObject<T: Mappable where T: Object>(completionHandler: (MyResponse<T> -> Void)) {
    responseObject { alamofireReponse in
        handleResponse(alamofireReponse, completionHandler: completionHandler)
    }
}

func myRequestArray<T: Mappable where T: Object>(completionHandler: (MyResponse<[T]> -> Void)) {
    responseArray { alamofireReponse in
        handleResponse(alamofireReponse, completionHandler: completionHandler)
    }
}

通过执行此操作,handleResponse ()方法和add ()方法将在编译时分配给适当的方法,具体取决于参数的类型。您无需动态检查类型。

整个代码如下:以防万一:

import Foundation

// Alamofire
enum AlamofireReponse<T, E> {
    case Success(T)
    case Failure(E)
}

func responseJSON(alamofireReponse: AlamofireReponse<AnyObject, NSError> -> Void) {
    alamofireReponse(.Success([["type": "not_object"], ["type": "not_object"]]))
}

// ObjectMapper
protocol Mappable {
    init()
}

// AlamofireObjectMapper
func responseObject<T: Mappable>(alamofireReponse: AlamofireReponse<T, NSError> -> Void) {
    alamofireReponse(.Success(T()))
}

func responseArray<T: Mappable>(alamofireReponse: AlamofireReponse<[T], NSError> -> Void) {
    alamofireReponse(.Success([T(), T()]))
}

// RealmSwift
class Object {}

func add(object: Object) {
    print("adding single object works") // This line would run
}

func add<T: SequenceType where T.Generator.Element: Object>(objects: T) {
    print("adding multiple objects works")  // This line would not
}

// My code
final class Post: Object, Mappable {}
final class Comment: Object, Mappable {}

enum MyResponse<T> {
    case Success(T)
    case Failure(NSError)
}

func myRequestJSON(completionHandler: (MyResponse<AnyObject> -> Void)) {
    responseJSON { alamofireReponse in
        handleResponse(alamofireReponse, completionHandler: completionHandler)
    }
}

func myRequestObject<T: Mappable where T: Object>(completionHandler: (MyResponse<T> -> Void)) {
    responseObject { alamofireReponse in
        handleResponse(alamofireReponse, completionHandler: completionHandler)
    }
}

func myRequestArray<T: Mappable where T: Object>(completionHandler: (MyResponse<[T]> -> Void)) {
    responseArray { alamofireReponse in
        handleResponse(alamofireReponse, completionHandler: completionHandler)
    }
}

func handleResponse(alamofireReponse: AlamofireReponse<AnyObject, NSError>, completionHandler: (MyResponse<AnyObject> -> Void)) {
    switch alamofireReponse {
    case .Success(let data):
        completionHandler(.Success(data))
    case .Failure(let error):
        completionHandler(.Failure(error))
    }
}

func handleResponse<T: Mappable where T: Object>(alamofireReponse: AlamofireReponse<T, NSError>, completionHandler: (MyResponse<T> -> Void)) {
    switch alamofireReponse {
    case .Success(let data):
        add(data)
        completionHandler(.Success(data))
    case .Failure(let error):
        completionHandler(.Failure(error))
    }
}

func handleResponse<T: Mappable where T: Object>(alamofireReponse: AlamofireReponse<[T], NSError>, completionHandler: (MyResponse<[T]> -> Void)) {
    switch alamofireReponse {
    case .Success(let data):
        add(data)
        completionHandler(.Success(data))
    case .Failure(let error):
        completionHandler(.Failure(error))
    }
}

// Usage
myRequestJSON { response in
}

myRequestObject { (response: MyResponse<Post>) in
}

myRequestArray { (response: MyResponse<[Post]>) in
}