符合协议和类的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
),可以考虑下面的方法:
-
声明一个从
ProtocolX
inheritance的附加协议,并附带ClassX
自动满足的方法/属性要求。 像下面的例子一样,一个方法和一个属性是额外的需求,UIViewController
自动满足这两个需求。protocol CustomTableViewDelegate: UITableViewDelegate { var navigationController: UINavigationController? { get } func performSegueWithIdentifier(identifier: String, sender: AnyObject?) }
-
声明一个附加协议,该协议从
ProtocolX
inheritance,具有ClassX
types的附加只读属性。 这种方法不仅可以完全使用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议将UIViewController
和UITableViewDelegate
混合在一起。
编辑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 } }