【问题标题】:Swift: How to check if an object of a generics type is an array of a subtype of a typeSwift:如何检查泛型类型的对象是否是类型的子类型的数组
【发布时间】:2016-11-24 16:44:51
【问题描述】:

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

我的问题是:如何检查泛型类型的对象是否是 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。我更喜欢“纯”Swift。

<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()])

【问题讨论】:

  • 我相信您上面的“简短版本”并未显示您实际询问的问题。在简短的版本中,我们似乎知道 Swift 类型系统在运行时知道我们的“泛型”是一个数组,其内容的类型(泛型)符合Protocol。检查这个包装类型是否是Parent 的子类是一项简单的任务(例如{ if let elem = array.first where elem is Parent { return true }; return false } 作为foo(...) -&gt; Bool 的主体)。然而,仔细观察你的要点,你有一个 data 类型为 AnyObject 的对象,它可能包含单个 ...
  • ... 对象实例或对象实例数组。与我们知道内容是一个数组相比,这是一个更棘手的情况(当 Swift 强类型“冲突”与 Cocoa 对象时经常发生这种情况),另一种选择是您使用的那个:尝试NSArray 强制转换。
  • 同意@dfri。这个问题实际上远比简短的版本复杂。但这是一个很好的起点,不是吗? :P
  • 将简短版本更改为 "func foo(array: T)" 使其工作但仍不符合预期。
  • 实际上它让我徒劳地发布了一个答案(我现在已经删除了),因为我认为它代表了你遇到的问题,我注意到(在仔细查看你的要点之后),它是真的不。最小的例子很好,因为在这种情况下,它似乎与您的实际问题有所不同,它可能只会让潜在的回答者(作为我自己)感到困惑>

标签: swift generics realm alamofire objectmapper


【解决方案1】:

在 Swift 类型系统中,[Child][Parent] 不相等,因为 Swift 的泛型不支持协变。您可以将重载与泛型类型约束一起使用,而不是子类型多态性和动态类型转换。

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

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
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-02-14
    • 2021-02-06
    • 1970-01-01
    • 2014-12-06
    • 2020-09-18
    • 1970-01-01
    • 2016-01-14
    • 1970-01-01
    相关资源
    最近更新 更多