【问题标题】:Swift generic class which could take Array of elements or single element可以采用元素数组或单个元素的 Swift 泛型类
【发布时间】:2016-05-05 22:30:19
【问题描述】:

我想创建一个能够采用 Parsable 类型或 Parsable 类型的 Array 的泛型类。两者的逻辑几乎相同,所以我不想为此操作创建两个不同的类。是否可以使用 Swift 泛型或协议关联类型类型来解决它?

protocol Parsable: class {
    associatedtype Type
    static func objectFromDictionary(dictionary: Dictionary<String, AnyObject>, inContext context: NSManagedObjectContext) -> Type?
    func importFromDictionary(dictionary: Dictionary<String, AnyObject>)
}

class ParseOperation<T: Parsable>: NSOperation {
    func execute() -> T {

    }
}

class ParseOperation<T where T: SequenceType, T.Generator.Element == Parsable>: NSOperation {
    func execute() -> T {
        // Do parsing
    }
}

这就是我想要的工作方式:

class ParseOperation<T where T: SequenceType, T.Generator.Element == Parsable OR T: Parsable>: NSOperation {
    func execute() -> T {
        // Do parsing
    }
}

在我当前的实现中,我使用的是看起来有点难看的枚举:

class ParseOperation<T where T: NSManagedObject, T:Parsable>: NSOperation {
    var responseToParse: AnyObject?
    var parseType: ParseType

    var parsedObjects: [T]?

    init(parseType: ParseType) {}

    func execute() {
        var objects: [NSManagedObject] = []

        if self.parseType == .Single {
            if let responseToParse = self.responseToParse as? Dictionary<String, AnyObject>,
                let parsedObject = T.objectFromDictionary(responseToParse, inContext: localContext) {
                objects.append(parsedObject)
            }
        } else if self.parseType == .Array {
            if let responseToParse = self.responseToParse as? Array<Dictionary<String, AnyObject>> {
                for dictionary in responseToParse {
                    if let parsedObject = T.objectFromDictionary(dictionary, inContext: localContext) {
                        objects.append(parsedObject)
                    }
                }
            }
        }
        self.parsedObjects = objects
        ...
    }
}

【问题讨论】:

  • 您需要展示您如何接受输入(两种情况都没有显示)以及输出差异是什么(您的两种情况返回一个 T,我不清楚这是否有意)。

标签: ios swift generics


【解决方案1】:

我修改了@RonaldMartin 的答案以显示ParsableArray 可能对您有什么帮助。不需要输入Parsable元素,只需这样实现parse函数即可:

protocol Parsable {
    associatedtype T

    static func parse(input: AnyObject) -> T?
}

struct ParsableArray<TElement where TElement: Parsable>: Parsable {
    static func parse(input: AnyObject) -> [TElement.T]? {
        guard let arrayInput = input as? [AnyObject] else {
            return nil
        }

        return arrayInput.flatMap(TElement.parse)
    }
}

我已将objectFromDictionary 重命名为parse,因为它需要AnyObject 而不是Dictionary 才能解析数组。当然,您可以将context 或您喜欢的任何内容添加到parse 方法中。

如果Parsable 这样做了,那么ParseOperation 就变得非常简单了:

class ParseOperation<T where T: Parsable>: NSOperation {
    let input: AnyObject

    var result: T.T?

    init(input: AnyObject) {
        self.input = input
    }

    override func main() {
        result = T.parse(input)
    }
}

然后,你可以这样解析数组(注意:这只是为了演示如何创建ParseOperationS只是一些Parsable结构):

let op = ParseOperation<ParsableArray<S>>(input: [["1": 5, "2": 6], ["3": 10]])
op.main()

var r: [S]? = op.result

我希望,这会有所帮助。

【讨论】:

    【解决方案2】:

    据我所知,类型约束系统并非旨在处理 OR 约束。但是,应该仍然可以按照您的要求进行操作。

    一种方法是将单例ParsablesParsables 的集合表示在一个可用于约束ParseOperation 的单一类型下。最好的方法是扩展Array(或SequenceTypeCollectionType等)以符合Parsable类型,但this is not yet possible从Xcode 7.3开始。您可以使用与该链接问题相同的解决方法,并添加一个中间类来表示 Parsable 数组:

    class ParsableArray: Parsable {
        let array: [Parsable]
    
        init(array: [Parsable]) {
            self.array = array
        }
    
        // Parsable conformance...
    }
    

    现在,您可以将原始协议用于类型约束:

    class ParseOperation<T: Parsable>: NSOperation {
        func execute() -> T {
            // Do parsing
        }
    } 
    

    【讨论】:

    • 这就是我现在期望它的工作方式,ParsableArray 采用 Parsable 对象数组,但还没有解析它,只是我指向我将解析数据的 Parsable 类,所以无法在此处传递 [Parsable] 对象。所以现在可能无法完成..
    • ParsableArray 也应该是通用的,否则你将失去类型安全
    【解决方案3】:

    理想情况下,您应该能够做到这一点:

    // This DOES NOT compile as of XCode 7.3
    extension Array: Parsable where Element: Parsable {
        // Parsable implementation
    }
    

    但是它目前不起作用


    目前你必须依赖一个包装结构:

    struct ParsableArray<Element: Parsable>: Parsable {
        let array: [Element]
        init(_ array: [Element]) {
            self.array = array
        }
    
        // Parsable implementation
    }
    

    您还可以为[Parsable] 数组实现一个方便的方法:

    extension Array where Element: Parsable {
        func toParsable() -> ParsableArray<Element> {
            return ParsableArray(self)
        }
    }
    

    所以你可以像这样执行你的方法:

    let array = [Parsable]()
    parse(array.toParsable()) // Equivalent to: parse(ParsableArray(array))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-10-25
      • 2017-05-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-21
      • 1970-01-01
      相关资源
      最近更新 更多