【发布时间】:2017-09-08 04:41:19
【问题描述】:
我有一个场景,我需要调用一个带有可选闭包参数的方法,其中闭包接收一个通用参数。这是我的简化代码:
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) -> Void' 不能转换为 '(T) -> Void'
不确定我的闭包定义是否对我正在尝试做的事情有意义。
更新代码:
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'
【问题讨论】:
-
这仍然行不通,因为你的闭包需要一个通用的 T。你需要类似
((FooModel) -> Void)?的东西(此时你可以摆脱泛型)但是在闭包中你会必须适当地将它转换为它应该是的任何东西。 (有很多方法可以解决这个问题,比如传入另一个参数以便知道它是什么类型等) -
@BadmintonCat 如果您发现自己编写了一个 switch 语句来处理泛型参数的类型,那么您就违背了泛型的全部目的,即抽象。使用
T的方法必须能够采用anyT,而不仅仅是您在方法中定义的一些狭窄列表。在我看来,重新考虑整个事情。 -
像这样的最小的东西:pastebin.com/0WDWh1Cb
-
另外,你的闭包永远不能是
nil,否则编译器将无法弄清楚T的具体类型是什么。如果你省略闭包并说onHTTPRequest(data: [:]),那么T是什么?您的代码将无法编译。
标签: swift generics swift3 closures