swift - 带有 Swift 协议(protocol)的通用类

标签 swift generics protocols generic-programming

我有一个像这样的基本协议(protocol):

public protocol BasicSwiftClassLayout { var nibName: String { get } }

我的课是这样的:

public class BasicSwiftClass<Layout: BasicSwiftClassLayout> {}

我定义了 2 个不同的结构,这样我就可以用 2 个不同的 nib 启动这个类:

public struct NormalLayout: BasicSwiftClassLayout { public let nibName = "NormalLayoutNibName" }

public struct OtherLayout: BasicSwiftClassLayout { public let nibName = "OtherLayoutNibName }

基于此,我现在有 2 个问题。

  1. 我想使用 Layout 变量来检索启动此类的 Nib 名称。因此,如果我以以下方式启动此类:let myView = BasicSwiftClass<NormalLayout>我希望能够在类(“NormalLayoutNibName”)中检索 NormalLayout 的 nibName。我想做类似 let myNib = Layout.nibName 的事情但它只是告诉我Instance member nibName cannot be used on type Layout那么我该如何检索 nibName?

  2. 当我没有将泛型类添加到我的类中时,它只是 public class BasicSwiftClass , classForCoder只是MyProject.BasicSwiftClass .现在我已经添加了通用行为,classForCoder返回为 MyProject.BasicSwiftClass<Layout.NormalLayout>在调用 let bundle = Bundle(for: classForCoder) 时不再返回正确的包我是否需要覆盖 classForCoder多变的?还是我在这里做错了什么?

谢谢!

最佳答案

由于您的 BasicSwiftClass 没有符合 BasicSwiftClassLayout 类型的实例 , 而不是只知道类型本身,你应该制作 nibName要求是静态的,因此它可以在类型本身上调用,而不是实例,如下所示:

public protocol BasicSwiftClassLayout {
    static var nibName: String { get }
}

public struct NormalLayout: BasicSwiftClassLayout {
   public static let nibName = "NormalLayoutNibName"
}

public struct OtherLayout: BasicSwiftClassLayout {
   public static let nibName = "OtherLayoutNibName"
}

关于你的第二个问题,我不认为Bundle(for class: AnyClass)初始化程序适用于泛型类,期间。如果有人找到了这样做的方法,我很高兴在这个问题上是错误的,但我的猜测是因为编译器生成泛型类型的具体变体,并且只生成那些实际使用的变体,这导致了一些挑战:

  • 通用类型的特定特化可以在任何导入通用类型并对通用类型具有可见性和访问权限的模块中使用/生成。例如,如果一个不同的模块导入了您的代码并定义了一个 public struct DifferentLayout: BasicSwiftClassLayout类型,会变体 BasicSwiftClass<DifferentLayout>成为您的 包的一部分,它定义了BasicSwiftClass 的所有逻辑,但在编译时没有那个变体,或者另一个模块导致编译器创建那个特化?

  • 您没有从技术上定义特殊类型 BasicSwiftClass<OtherLayout> ,编译器根据确定将在哪里使用它来做。因此,它真正属于哪个 bundle 可能会变得模糊。

我猜想如果有一种方法可以引用通用类型 本身,即一个未专门化的 BasicSwiftClass<Layout>输入引用,然后输入 Bundle(for:)会正常工作。但是没有办法像那样引用泛型类型,因为它更像是供编译器用于生成具体特化的模板,而不是运行时存在的实际类型。

建议的替代方法:

如果您使协议(protocol)需要一个类类型来实现一致性,您可以获得布局而不是 BasicSwiftClass 的包。例如:

public protocol BasicSwiftClassLayout: class {
    static var nibName: String { get }
}

final public class NormalLayout: BasicSwiftClassLayout {
   public static let nibName = "NormalLayoutNibName"
}

public class BasicSwiftClass<Layout: BasicSwiftClassLayout> {
    func loadFromNib() {
        let view =  Bundle(for: Layout.self).loadNibNamed(Layout.nibName, owner: self, options: nil)?.first
    }
}

创建泛型数组

在回答您有关如何制作 BasicSwiftClass<Layout> 数组的问题时Layout 具有不同的值,根据您需要如何使用从数组中检索到的元素,有不同的方法可以解决这个问题。最终,Swift 要求集合(包括数组)包含的元素都是相同类型或可以作为相同的通用类型处理,因此所有不同的方法都基于转换、包装或强制转换每个 BasicSwiftClass<Layout>实例到一些公共(public)类型,公开您要在每个实例上调用的公共(public)功能。

最简单的解决方案可能是创建一个协议(protocol) BasicSwiftClass<Layout>符合,它公开了您需要的功能。例如,如果您只关心从每个实例获取一个 View 以添加到 View 层次结构中,您可以这样做:

public protocol ViewRepresentable {
    var view: UIView { get }
}

extension BasicSwiftClass<Layout>: ViewRepresentable {
   public var view: UIView { // load the nib, connect outlets and do other setup, then return the appropriate UIView }
}

let array: [ViewRepresentable] = [BasicSwiftClass<IPadLayout>, BasicSwiftClass<NormalLayout>]
array.forEach { self.containerView.addSubview($0.view) }

如果您需要更专业的东西或在内部保留某些类型信息等,您可能需要使用包装器或类型橡皮擦。您也可以尝试使用 BasicSwiftClass<Layout>从具有您需要的基本功能的非泛型父类(super class)继承,并将集合键入为该父类(super class)类型的实例数组。但是,如果您可以在非通用协议(protocol)中表达共同需求,那应该会让您走上最简单的道路。

关于swift - 带有 Swift 协议(protocol)的通用类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47874304/

相关文章:

ios - Apple 真实支付的应用内购买测试

java - 通用列表和反射(reflection)

ios - 如何在 Swift 中使用扩展协议(protocol)公开 Obj-C 类的现有属性

swift - 通用 Controller 内的通用 Swift 协议(protocol)

ios - 在后台快速更新用户位置

objective-c - 如何将 NSDitonary 从 Swift 转换为 Objective-c?

swift - Swift 中的多个连接 - 如何确定按下了哪个按钮?

java - 使用相同接口(interface)变量的多种类型实现

.net - 为什么 Nullable<T> 不实现 IComparable?

ios - Swift 2.2 泛型 : "Cannot convert return expression of type ItemPageControllerFactory to return type T"