0

我正在尝试在 Swift 中创建一个可用于对象构造的协议。我遇到的问题是我需要存储类型信息,以便以后可以构造类型并在回调中返回。我似乎找不到一种方法来存储它而不会导致编译器崩溃或产生构建错误。这是基础知识(一个人为但有效的示例):

protocol Model {
  init(values: [String])
  func printValues()
}

struct Request<T:Model> {
  let returnType:T.Type
  let callback:T -> ()
}

我们有一个简单的协议,它声明了一个init(用于构造)和另一个 func printValues()(用于测试)。我们还定义了一个结构,我们可以使用它来存储类型信息和一个回调,以便在构造新类型时返回它。

接下来我们创建一个构造函数:

class Constructor {
  var callbacks: [Request<Model>] = []

  func construct<T:Model>(type:T.Type, callback: T -> ()) {
    callback(type(values: ["value1", "value2"]))
  }

  func queueRequest<T:Model>(request: Request<T>) {
    callbacks.append(request)
  }

  func next() {
    if let request = callbacks.first {
      let model = request.returnType(values: ["value1", "value2"])
      request.callback(model)
    }
  }
}

需要注意的几件事:这会导致编译器崩溃。由于某种原因,它无法弄清楚这一点。问题似乎是var callbacks: [Request<Model>] = []。如果我注释掉其他所有内容,编译器仍然会崩溃。注释掉 var 回调并且编译器停止崩溃。

此外,func construct工作正常。但它不存储类型信息,所以它对我来说不是那么有用。我放在那里进行演示。

我发现如果我从 Request struct: 中删除协议要求,我可以防止编译器崩溃struct Request<T>。在这种情况下,一切正常并编译,但我仍然需要let model = request.returnType(values: ["value1", "value2"])func next(). 这也导致编译器崩溃。

这是一个使用示例:

func construct() {
  let constructor = Constructor()
  let request = Request(returnType: TypeA.self) { req in req.printValues() }

  //This works fine
  constructor.construct(TypeA.self) { a in
    a.printValues()
  }

  //This is what I want
  constructor.queueRequest(request)
  constructor.next() //The callback in the request object should be called and the values should print
}

有谁知道我如何将类型信息存储在特定协议中,以便以后可以动态构造并在回调中返回?

4

4 回答 4

1

如果您想要完全相同的行为,next我建议您这样做:

class Constructor {
  // store closures
  var callbacks: [[String] -> ()] = []

  func construct<T:Model>(type:T.Type, callback: T -> ()) {
    callback(type(values: ["value1", "value2"]))
  }

  func queueRequest<T:Model>(request: Request<T>) {
    // some code from the next function so you don't need to store the generic type itself
    // **EDIT** changed closure to type [String] -> () in order to call it with different values
    callbacks.append({ values in
      let model = request.returnType(values: values)
      request.callback(model)
    })
  }

  func next(values: [String]) {
    callbacks.first?(values)
  }
}

现在你可以next用你的价值观打电话了。希望这对你有用。

编辑:对闭包类型和next功能进行了一些更改

于 2015-06-19T14:52:44.443 回答
0

不幸的是,没有办法将特定的泛型类型保存在数组中并动态调用它们的方法,因为 Swift 是一种静态类型语言(并且Array必须具有明确的类型)。

但希望我们将来可以这样表达:

var callbacks: [Request<T: Model>] = []

例如,哪里T可以是任何东西,但必须符合Model

于 2015-06-18T23:40:26.410 回答
0

您的queueRequest方法不必知道Request它正在传递的泛型类型。由于callbacks是一个Request<Model>类型数组,该方法只需要知道排队的请求是 type Request<Model>。泛型类型是什么并不重要。

这段代码在 Playground 中为我构建:

class Constructor {
  var callbacks: [Request<Model>] = []

  func construct<T:Model>(type:T.Type, callback: T -> ()) {
    callback(type(values: ["value1", "value2"]))
  }

  func queueRequest(request: Request<Model>) {
    callbacks.append(request)
  }

  func next() {
    if let request = callbacks.first {
      let model = request.returnType(values: ["value1", "value2"])
      request.callback(model)
    }
  }
}
于 2015-06-19T00:12:59.087 回答
0

所以我找到了一个似乎完全符合我要求的答案。我还没有在实时代码中确认这是否有效,但它确实编译没有任何错误。事实证明,我需要再添加一级重定向:

我为对象构造明确创建了另一个协议:

protocol ModelConstructor {
  func constructWith(values:[String])
}

在我的 Request 结构中,我遵循这个协议:

struct Request<T:Model> : ModelConstructor {
  let returnType:T.Type
  let callback:T -> ()

  func constructWith(values:[String]) {
    let model = returnType(values: values)
    callback(model)
  }
}

请注意,实际构造已移至Request结构中。从技术上讲,Constructor它不再是在构建,但现在我不理会它的名字。我现在可以将Request结构存储为ModelConstructor并正确排队请求:

class Constructor {
  var callbacks: [ModelConstructor] = []

  func queueRequest(request: Request<Model>) {
    queueRequest(request)
  }

  func queueRequest(request: ModelConstructor) {
    callbacks.append(request)
  }

  func next() {
    if let request = callbacks.first {
      request.constructWith(["value1", "value2"])
      callbacks.removeAtIndex(0)
    }
  }
}

请注意这里的一些特别之处:我现在可以成功“排队”(或存储在数组中)Request<Model>,但我必须通过调用queueRequest(request: ModelConstructor). 在这种情况下,我正在超载,但这不是必需的。这里重要的是,如果我尝试调用callbacks.append(request)queueRequest(request: Request<Model>)函数,Swift 编译器会崩溃。显然我们需要在这里稍微握住编译器的手,这样它才能理解我们到底想要什么。

我发现您无法将类型信息与类型构造分开。它需要都在同一个地方(在这种情况下是Request结构)。但是只要您将构造与类型信息结合起来,您就可以自由地延迟/存储构造,直到您拥有实际构造对象所需的信息。

于 2015-06-23T03:40:18.977 回答