符合协议和类的Swift属性

@property (strong, nonatomic) UIViewController<UITableViewDelegate> *thing; 

我想在Swift中实现一个像这个Objective-C代码一样的属性。 所以这里是我试过的:

 class AClass<T: UIViewController where T: UITableViewDelegate>: UIViewController { var thing: T! } 

这编译。 我从故事板添加属性时出现问题。 @IBOutlet标签会生成一个编译器错误。

 class AClass<T: UIViewController where T: UITableViewDelegate>: UIViewController { @IBOutlet weak var anotherThing: UILabel! // error var thing: T! } 

错误:

 Variable in a generic class cannot be represented in Objective-C 

我正在执行这个权利? 我能做些什么来修复或解决这个错误?

编辑:

Swift 4终于有了解决这个问题的办法。 看到我更新的答案。

更新Swift 4

Swift 4增加了将types表示为符合协议的类的支持。 语法是Class & Protocol 。 下面是一些使用“Swift新特性”(WWDC 2017会话402)中的概念的代码示例:

 protocol Shakeable { func shake() } extension UIButton: Shakeable { /* ... */ } extension UISlider: Shakeable { /* ... */ } // Example function to generically shake some control elements func shakeEm(controls: [UIControl & Shakeable]) { for control in controls where control.state.isEnabled { control.shake() } } 

从Swift 3开始,这个方法会导致问题,因为你不能传入正确的types。 如果您尝试通过[UIControl] ,它没有shake方法。 如果你尝试传入[UIButton] ,那么代码将被编译,但是你不能传入任何UISlider 。 如果你传入[Shakeable] ,那么你不能检查control.state ,因为Shakeable没有。 斯威夫特4终于解决了这个话题。

老答案

我正在解决这个问题,暂时用下面的代码:

 // This class is used to replace the UIViewController<UITableViewDelegate> // declaration in Objective-C class ConformingClass: UIViewController, UITableViewDelegate {} class AClass: UIViewController { @IBOutlet weak var anotherThing: UILabel! var thing: ConformingClass! } 

这对我来说似乎很难受。 如果需要任何委托方法,那么我将不得不在ConformingClass (我不想这样做)中实现这些方法,并在子类中重写它们。

我发布了这个答案,以防其他人遇到这个问题,我的解决scheme可以帮助他们,但我不满意解决scheme。 如果有人提出更好的解决scheme,我会接受他们的答案。

这不是一个理想的解决scheme,但是可以使用generics函数而不是generics类,如下所示:

  class AClass: UIViewController { @IBOutlet weak var anotherThing: UILabel! private var thing: UIViewController? func setThing<T: UIViewController where T: UITableViewDelegate>(delegate: T) { thing = delegate } } 

我遇到了同样的问题,也尝试了通用的方法。 最终通用的方法打破了整个devise。

在重新思考这个问题之后,我发现一个不能用来完全指定types的协议(换句话说,必须附加types信息,比如类types)不太可能是完整的。 而且,尽pipe声明ClassType<ProtocolType>的Objc风格来得方便,但它忽略了协议提供的抽象的好处,因为这样的协议并没有真正提高抽象级别。 此外,如果这种声明出现在多个地方,则必须重复。 更糟糕的是,如果这种types的多个声明是相互关联的(可能会传递一个单一的对象),程序变得脆弱和难以维护,因为稍后如果在一个地方的声明需要改变,所有相关的声明必须也要改变。

如果一个属性的用例涉及一个协议(比如ProtocolX )和一个类的某些方面(比如ClassX ),可以考虑下面的方法:

  1. 声明一个从ProtocolXinheritance的附加协议,并附带ClassX自动满足的方法/属性要求。 像下面的例子一样,一个方法和一个属性是额外的需求, UIViewController自动满足这两个需求。

     protocol CustomTableViewDelegate: UITableViewDelegate { var navigationController: UINavigationController? { get } func performSegueWithIdentifier(identifier: String, sender: AnyObject?) } 
  2. 声明一个附加协议,该协议从ProtocolXinheritance,具有ClassXtypes的附加只读属性。 这种方法不仅可以完全使用ClassX ,而且还具有不需要执行子类ClassX的灵活性。 例如:

     protocol CustomTableViewDelegate: UITableViewDelegate { var viewController: UIViewController { get } } // Implementation A class CustomViewController: UIViewController, UITableViewDelegate { var viewController: UIViewController { return self } ... // Other important implementation } // Implementation B class CustomClass: UITableViewDelegate { private var _aViewControllerRef: UIViewController // Could come from anywhere eg initializer var viewController: UIViewController { return _aViewControllerRef } ... // UITableViewDelegate methods implementation } 

PS。 上面的代码片段仅供演示,不build议将UIViewControllerUITableViewDelegate混合在一起。

编辑Swift 2+:感谢@ Shaps的评论,可以添加以下内容来保存无处不在的实现所需的属性。

 extension CustomTableViewDelegate where Self: UIViewController { var viewController: UIViewController { return self } } 

你可以像这样在Swift中声明一个委托:

 weak var delegate : UITableViewDelegate? 

它将与混合(Objective-C和Swift)项目一起工作。 代表应该是可选的和弱的,因为它的可用性是不能保证的,弱也不会产生保留周期。

你正在得到这个错误,因为在Objective-C中没有generics,并且不允许你添加@IBOutlet属性。

编辑:1.强制委派的types

强制该委托总是一个UIViewController你可以实现自定义设置和抛出exception,当它不是一个UIViewController。

 weak var _delegate : UITableViewDelegate? //stored property var delegate : UITableViewDelegate? { set { if newValue! is UIViewController { _delegate = newValue } else { NSException(name: "Inavlid delegate type", reason: "Delegate must be a UIViewController", userInfo: nil).raise() } } get { return _delegate } }