gpt4 book ai didi

ios - Swift 泛型类、继承和协方差

转载 作者:搜寻专家 更新时间:2023-11-01 06:58:14 24 4
gpt4 key购买 nike

我面临着使用泛型类和继承的问题。

问题简述:

我有一个名为 BookPageDataSource 的基类和两个具有不同实现的继承类( ReadingBookPageDataSourceStarsBookPageDataSource )。

另外,我有一个通用类 BookPageViewController包含此数据源的通用参数和从此类继承的两个类( ReadingBookPageViewControllerStarsBookPageViewController )。

我需要写一个方法,它的返回参数是BookPageViewController<DataSource> .

// Data Sources

class BookPageDataSource { }

class ReadingBookPageDataSource: BookPageDataSource { }

class StarsBookPageDataSource: BookPageDataSource { }

// Controllers

class BookPageViewController<DataSource: BookPageDataSource>: UIViewController {
let dataSource: DataSource

init(dataSource: DataSource) {
self.dataSource = dataSource

super.init(nibName: nil, bundle: nil)
}

required init?(coder aDecoder: NSCoder) {
return nil
}
}

final class ReadingBookPageViewController: BookPageViewController<ReadingBookPageDataSource> { }

final class StarsBookPageViewController: BookPageViewController<StarsBookPageDataSource> { }

// Communication

class Pager {
func currentPageController<DataSource>(at index: Int) -> BookPageViewController<DataSource> {
// for example

if index == 0 {
// How to remove the cast from the line below?
return readingPageController() as! BookPageViewController<DataSource>
}

return starsPageController() as! BookPageViewController<DataSource>
}

private func readingPageController() -> ReadingBookPageViewController {
return ReadingBookPageViewController(dataSource: ReadingBookPageDataSource())
}

private func starsPageController() -> StarsBookPageViewController {
return StarsBookPageViewController(dataSource: StarsBookPageDataSource())
}
}

方法 currentPageController总是崩溃,因为数据源总是等于 BookPageDataSource , 不至 ReadingBookPageDataSourceStarsBookPageDataSource .

最佳答案

概念讨论

您对架构的概念有缺陷,这导致了您的问题。

简单的泛型示例

这是一个非常简单的泛型函数示例,它只返回您给它的值:

func echo <T> (_ value: T) -> T { return value }

因为这个函数是通用的,所以它使用的类型有歧义。什么是 T ? Swift 是一种类型安全的语言,这意味着最终不允许有任何关于类型的歧义。那么为什么允许这种回声功能呢?答案是当我在某处实际使用这个函数时,类型的歧义将被消除。例如:
let myValue = echo(7)      // myValue is now of type Int and has the value 7

在使用这个通用函数的过程中,我通过传递一个 Int 来消除歧义。 ,因此编译器对所涉及的类型没有不确定性。

你的职能
func currentPageController <DataSource> (at index: Int) -> BookPageViewController<DataSource>

您的函数仅使用通用参数 DataSource在返回类型中,而不是在输入中 - 编译器应该如何弄清楚 DataSource是?* 我想这就是你想象中使用你的函数的方式:
let pager = Pager()
let controller = pager.currentPageController(at: 0)

但是现在, controller的类型是什么? ?你能期望用它做什么?您似乎希望 controller将根据您传入的值( 0 )采用正确的类型,但这不是它的工作方式。泛型参数是根据输入的类型确定的,而不是输入的值。你希望通过 0将产生一种返回类型,而 1将产生不同的结果 - 但在 Swift 中这是禁止的。两者 01属于 Int 类型,而类型才是最重要的。

与 Swift 通常的情况一样,阻止您做某事的并不是语言/编译器。这是因为您还没有从逻辑上表述出您想要的甚至是什么,而编译器只是告诉您到目前为止您所编写的内容没有意义。

解决方案

不过,让我们继续为您提供解决方案。

UIViewController 功能

大概有你想要使用的东西 controller为了。你真正需要的是什么?如果你只是想把它推到导航 Controller 上,那么你不需要它是一个 BookPageViewController .你只需要它是一个 UIViewController使用该功能,所以你的功能可以变成这样:
func currentPageController (at index: Int) -> UIViewController {
if index == 0 {
return readingPageController()
}
return starsPageController()
}

您可以将它返回的 Controller 推送到导航堆栈上。

自定义功能(非通用)

但是,如果您需要使用某些特定于 BookPageViewController 的功能。那么这取决于你想要做什么。如果 BookPageViewController上有方法像这样:
func doSomething (input: Int) -> String

不使用通用参数 DataSource那么您可能希望将该函数分离为它自己的非通用协议(protocol)/父类(super class)。例如:
protocol DoesSomething {
func doSomething (input: Int) -> String
}

然后有 BookPageViewController符合它:
extension BookPageViewController: DoesSomething {
func doSomething (input: Int) -> String {
return "put your implementation here"
}
}

现在你的函数的返回类型可以是这个非通用协议(protocol):
func currentPageController (at index: Int) -> DoesSomething {
if index == 0 {
return readingPageController()
}
return starsPageController()
}

你可以像这样使用它:
let pager = Pager()
let controller = pager.currentPageController(at: 0)
let retrievedValue = controller.doSomething(input: 7)

当然,如果返回类型不再是 UIViewController无论如何,您可能要考虑重命名函数和相关变量。

自定义功能(通用)

另一种选择是你不能将你需要的功能分离到一个非通用的协议(protocol)/父类(super class)中,因为这个功能使用了通用参数 DataSource .一个基本的例子是:
extension BookPageViewController {
func setDataSource (_ newValue: DataSource) {
self.dataSource = newValue
}
}

所以在这种情况下,你确实需要函数的返回类型是 BookPageViewController<DataSource> .你做什么工作?好吧,如果您真正想要的是使用 setDataSource(_:)上面定义的方法那么你必须有一个 DataSource您打算作为参数传入的对象,对吗?如果是这种情况,那么我们正在取得进展。以前,您只有一些 Int您传递给函数的值,问题是您无法用它指定通用返回类型。但是如果你已经有了 BookPageDataSource值(value)那么至少在逻辑上您可以使用它来专门化您的
功能。

但是,您所说的只是使用 Int无论 DataSource 是什么,都可以在该索引处获取 Controller 类型是。但如果你不在乎什么 DataSource是返回的 BookPageViewController那么你怎么能期望设置它的 DataSource使用 setDataSource(_:) 到其他东西方法?

你看,问题自己解决了。您需要函数的返回类型为泛型的唯一原因是,您需要使用的后续功能是否使用该泛型类型,但如果是这种情况,那么您返回的 Controller 不能有任何旧的 DataSource (您只想要与您提供的索引相对应的任何一个)-您需要它具有完全相同的类型 DataSource你打算在使用它时传入它,否则你给它的类型是错误的。

因此,您问题的最终答案是,按照您的设想,您尝试构建的函数没有可能的用途。 Swift 的架构方式非常酷的是,编译器实际上能够找出逻辑缺陷并阻止您构建代码,直到您重新对其进行概念化。

脚注:

* 可以有一个泛型函数,它只在返回类型中使用泛型参数而不在输入中使用,但这在这里无济于事。

关于ios - Swift 泛型类、继承和协方差,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52111488/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com