Swift:属性符合特定类,同时符合多个协议

Yan*_*iot 31 protocols swift

在Objective-C中,可以编写类似的东西:

@property(retain) UIView<Protocol1, Protocol2, ...> *myView;
Run Code Online (Sandbox Code Playgroud)

但是如何在swift中编写这段代码呢?

我已经知道如何使属性符合许多协议,但它不能使用继承:

var myView: ??? protocol<Protocol1, Protocol2, ...>
Run Code Online (Sandbox Code Playgroud)

编辑:

我使用许多UIView子类型UIImageView,UILabel或者其他类型,我需要使用一些UIView属性以及协议中定义的一些方法.在最坏的情况下,我可以UIViewProtocol使用所需的属性创建一个,但我知道在Swift中是否可以声明一个属性/变量,其类型和一些协议符合.

Mik*_*e S 25

您可以使用where子句对泛型类执行此操作:

where子句使您能够要求关联类型符合某个协议,和/或某些类型参数和相关类型是相同的.

要使用它,请在具有类型约束的泛型类中定义属性,以检查属性的类型参数是否与所需的基类和协议匹配.

对于您的具体示例,它可能看起来像这样:

class MyViewController<T where T: UIView, T: Protocol1, T: Protocol2>: UIViewController {
    var myView: T

    // ...
}
Run Code Online (Sandbox Code Playgroud)

  • 要使用此解决方案,如果初始化控制器,则必须指定T. 如果属性中只有一个对象,那没问题,但在我的情况下,我有一个数组,它可以包含从基类的不同子类实例化的多个对象.@MikeS你会如何解决这个问题? (6认同)

Vla*_*tko 6

在Swift 4中,它终于成为可能了.您可以同时声明某些符合协议的类的变量,如下所示:

class ClassA {
    var someVar: String?
}

protocol ProtocolA {}

class ClassB {
    var someOptional: (ClassA & ProtocolA)? // here is optional value
    var some: ClassA & ProtocolA // here is non-optional value; need to provide init though :)
}
Run Code Online (Sandbox Code Playgroud)


Sla*_*bko 5

一个也可能有点丑陋的方法之一是创建一个包装器协议UIView:

protocol UIViewRef {
    var instance: UIView { get }
}
Run Code Online (Sandbox Code Playgroud)

现在可以创建一个实现的协议Protocol1,Protocol2并且UIViewRef将用于获取UIView自身:

protocol MyUIViewProtocol: UIViewRef, Protocol1, Protocol2 { }
Run Code Online (Sandbox Code Playgroud)

最后一步将为您的UIViews 实现UIViewRef协议,在您的情况下,据我所知,已经实现Protocol1并且Protocol2:

// SomeOfMyViews already implements Protocol1 and Protocol2
extension SomeOfMyUIViews: MyUIViewProtocol {
    var instance: UIView { return self }
}
Run Code Online (Sandbox Code Playgroud)

作为我们的结果MyUIViewProtocol,其实施者持有对a的引用,UIView并且每个实现Protocol1和实现 Protocol2.但有一点需要注意 - 为了得到它UIView自己,我们需要从instance 财产中提出它的参考.例如

// Lets say we're somewhere in a UIViewController
var views: [SomeOfMyUIView] = // Get list of my views
views.forEach { self.view.addSubview($0.instance) }
Run Code Online (Sandbox Code Playgroud)