我有一个场景,我需要调用一个带有可选闭包参数的方法,其中闭包接收泛型参数。这是我的简化代码:
class FooModel
{
}
class FooSubClass1 : FooModel
{
}
class FooSubClass2 : FooModel
{
}
class Client
{
func httpGet<T:FooModel>(closure:((T) -> Void)? = nil)
{
// Doing some network request stuff here and call onHTTPRequestComplete() when done!
onHTTPRequestComplete(data: result, closure: closure)
}
func onHTTPRequestComplete<T:FooModel>(data:[String:Any], closure:((T) -> Void)? = nil)
{
if let c = closure
{
switch(T)
{
case is FooSubClass1:
var foo = FooSubClass1()
// Process data into new FooSubClass1 object here!
c(foo)
case is FooSubClass2:
var foo = FooSubClass2()
// Process data into new FooSubClass2 object here!
c(foo)
}
}
}
}
class App
{
func someFunc()
{
client.httpGet()
{
response in
print("\(response)")
}
}
}
我在这里要做的是:
我有一个带有超类和几个子类的数据模型。在Client
中,我执行http请求以检索数据,并希望使用检索到的数据填充模型对象。 App
中的调用方法应该能够提供在异步网络请求完成后调用的闭包,并在其中返回正确的数据模型对象。
我的代码有几个问题:
switch(T)
:错误:类型名称后的预期成员名称或构造函数调用
c(foo)
:错误:'(FooSubClass1) - &gt; Void'不能转换为'(T) - &gt;无效“
不确定我的闭包定义对我正在尝试做的事情有意义。
更新代码:
protocol FooModel
{
init()
}
class FooModelBase : FooModel
{
}
class FooSubClass1 : FooModelBase
{
}
class FooSubClass2 : FooModelBase
{
}
class Client
{
func httpGet<T:FooModel>(closure:((T) -> Void)? = nil)
{
// Doing some network request stuff here and call onHTTPRequestComplete() when done!
onHTTPRequestComplete(data: result, closure: closure)
}
func onHTTPRequestComplete<T:FooModel>(data:[String:Any], closure:((T) -> Void)? = nil)
{
if let c = closure
{
switch T.self
{
case is FooSubClass1.Type:
var foo = FooSubClass1()
// Assign retrieved values to model!
closure(foo)
case is FooSubClass2.Type:
var foo = FooSubClass2()
// Assign retrieved values to model!
closure(foo)
default:
break
}
}
}
}
class App
{
func someFunc()
{
client.httpGet()
{
response in
print("\(response)") // Should output FooSubClass1 instance with assigned values!
}
}
}
几乎可以正常工作,但我收到以下编译错误:
'closure(foo): Error: '(@lvalue FooSubClass1) -> Void' is not convertible to '(T) -> Void'
答案 0 :(得分:1)
对于switch(T)
错误,您当然会收到此错误。因为您正在尝试切换类型,但您应该切换一个值。对于你的第二个错误它是完全合理的,因为那时闭包会期待什么因为T是通用的。
有关闭包中泛型的更多信息,请查看此stackoverflow线程
generics as parameters to a closure in swift
总而言之,你在那里尝试做的事情是不可能的,乍一看我相信这不是正确的方向(虽然我没有看到整个背景)。你可以做的一件事可能是你的闭包是((Any) -> Void)?
,但不幸的是,类型将是Any
,你必须在闭包内进行切换。
答案 1 :(得分:1)
闭包中T
的值将传递给闭包本身。你无法打开它。但我认为你想要的是打开类型。你可以这样做:
let type = String(reflecting: T.self)
switch (type) {
case "Module.FooSubClass1":
var foo = FooSubClass1()
}
或者你可以说:
if FooSubClass1.self == T.self {
var foo = FooSubClass1();
} else if FooSubClass2.self == T.self {
var foo = FooSubClass2();
}
然而,this code smells。你似乎想要使用泛型,但是在你的泛型方法中有一组硬编码的类,这一半会破坏泛型的整个目的。为什么不将FooModel
定义为
protocol FooModel {
init()
}
然后代替你的switch语句,只需创建一个并将其传递给你的闭包。
func onHTTPRequestComplete<T: FooModel>(data: [String: Any], closure: ((T) -> Void)? = nil) {
guard let closure = closure else { return }
closure(T())
}
或者为什么不在T
之外创建onHTTPRequestComplete
并传递它?
onHTTPRequestComplete(data: data: model: FooSubClass1()) { model in
// Do something awesome with `model` right here.
}
然而,我可能会这样做:
func httpGet(callback: (([String: Any]) -> Void)? = nil) {
// Do some network stuff and get the data
callback?(data)
}
然后在callback
内,只需创建你想要的任何类型:
httpGet { data in
let foo = FooSubClass1(data: data)
// foo on you
}