Swift 属性 符合协议并且 Class
Swift Property that conforms to a Protocol and Class
@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
我执行得对吗?我该怎么做才能修复或绕过此错误?
编辑:
Swift4 这个问题终于有了解决办法。查看我更新的答案。
您可以像这样在 Swift 中声明委托:
weak var delegate : UITableViewDelegate?
它甚至可以与混合(Objective-c 和 swift)项目一起使用。委托应该是可选的和弱的,因为不能保证它的可用性并且弱不会创建保留周期。
您收到该错误是因为 Objective-C 中没有泛型,并且不允许您添加 @IBOutlet 属性。
编辑:1. 强制委托类型
要强制该委托始终是 UIViewController,您可以实现自定义 setter 并在它不是 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
}
}
更新 Swift 4
Swift 4 添加了对将类型表示为符合协议的 class 的支持。语法是 Class & Protocol
。以下是使用 "What's New in 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.isEnabled {
control.shake()
}
}
从 Swift 3 开始,此方法会导致问题,因为您无法传入正确的类型。如果您尝试传入 [UIControl]
,它没有 shake
方法。如果您尝试传入 [UIButton]
,则代码会编译,但您无法传入任何 UISlider
。如果你传入 [Shakeable]
,那么你无法检查 control.state
,因为 Shakeable
没有那个。 Swift4终于说正题了
旧答案
我暂时使用以下代码解决了这个问题:
// 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
中实现这些方法(我不想这样做)并在 subclass.
中覆盖它们
我已经发布了这个答案,以防其他人遇到这个问题,我的解决方案可以帮助他们,但我对这个解决方案不满意。如果有人发布更好的解决方案,我会采纳他们的回答。
这不是理想的解决方案,但您可以使用通用函数代替通用函数 class,如下所示:
class AClass: UIViewController {
@IBOutlet weak var anotherThing: UILabel!
private var thing: UIViewController?
func setThing<T: UIViewController where T: UITableViewDelegate>(delegate: T) {
thing = delegate
}
}
我遇到了同样的问题,也尝试了通用方法。最终,通用方法破坏了整个设计。
重新思考这个问题后,我发现一个协议不能完全指定一个类型(换句话说,必须带有额外的类型信息,例如 class 类型)的可能性不大成为一个完整的人。此外,虽然声明 ClassType<ProtocolType>
的 Objc 风格很方便,但它忽略了协议提供的抽象的好处,因为这种协议并没有真正提高抽象级别。此外,如果此类声明出现在多个地方,则必须重复。更糟糕的是,如果这种类型的多个声明是相互关联的(可能会在它们周围传递一个对象),程序会变得脆弱且难以维护,因为以后如果需要更改一个地方的声明,则所有相关的声明都必须更改也被改变了。
解决方案
如果 属性 的用例既涉及协议(例如 ProtocolX
)又涉及 class 的某些方面(例如 ClassX
),则采用以下方法可以考虑:
声明一个附加协议,该协议继承自 ProtocolX
,并添加了 ClassX
自动满足的 method/property 要求。像下面的例子,一个方法和一个属性是额外的要求,UIViewController
自动满足。
protocol CustomTableViewDelegate: UITableViewDelegate {
var navigationController: UINavigationController? { get }
func performSegueWithIdentifier(identifier: String, sender: AnyObject?)
}
声明一个从 ProtocolX
继承的附加协议,带有 ClassX
类型的附加只读 属性。这种方法不仅允许完整地使用 ClassX
,而且还展示了不需要实现 subclass 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 e.g. initializer
var viewController: UIViewController { return _aViewControllerRef }
... // UITableViewDelegate methods implementation
}
PS. 以上代码片段仅供演示,不建议将 UIViewController
和 UITableViewDelegate
混合使用。
编辑 Swift 2+: 感谢@Shaps 的评论,可以添加以下内容以节省必须在所有地方实现所需的 属性。
extension CustomTableViewDelegate where Self: UIViewController {
var viewController: UIViewController { return self }
}
@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
我执行得对吗?我该怎么做才能修复或绕过此错误?
编辑:
Swift4 这个问题终于有了解决办法。查看我更新的答案。
您可以像这样在 Swift 中声明委托:
weak var delegate : UITableViewDelegate?
它甚至可以与混合(Objective-c 和 swift)项目一起使用。委托应该是可选的和弱的,因为不能保证它的可用性并且弱不会创建保留周期。
您收到该错误是因为 Objective-C 中没有泛型,并且不允许您添加 @IBOutlet 属性。
编辑:1. 强制委托类型
要强制该委托始终是 UIViewController,您可以实现自定义 setter 并在它不是 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
}
}
更新 Swift 4
Swift 4 添加了对将类型表示为符合协议的 class 的支持。语法是 Class & Protocol
。以下是使用 "What's New in 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.isEnabled {
control.shake()
}
}
从 Swift 3 开始,此方法会导致问题,因为您无法传入正确的类型。如果您尝试传入 [UIControl]
,它没有 shake
方法。如果您尝试传入 [UIButton]
,则代码会编译,但您无法传入任何 UISlider
。如果你传入 [Shakeable]
,那么你无法检查 control.state
,因为 Shakeable
没有那个。 Swift4终于说正题了
旧答案
我暂时使用以下代码解决了这个问题:
// 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
中实现这些方法(我不想这样做)并在 subclass.
我已经发布了这个答案,以防其他人遇到这个问题,我的解决方案可以帮助他们,但我对这个解决方案不满意。如果有人发布更好的解决方案,我会采纳他们的回答。
这不是理想的解决方案,但您可以使用通用函数代替通用函数 class,如下所示:
class AClass: UIViewController {
@IBOutlet weak var anotherThing: UILabel!
private var thing: UIViewController?
func setThing<T: UIViewController where T: UITableViewDelegate>(delegate: T) {
thing = delegate
}
}
我遇到了同样的问题,也尝试了通用方法。最终,通用方法破坏了整个设计。
重新思考这个问题后,我发现一个协议不能完全指定一个类型(换句话说,必须带有额外的类型信息,例如 class 类型)的可能性不大成为一个完整的人。此外,虽然声明 ClassType<ProtocolType>
的 Objc 风格很方便,但它忽略了协议提供的抽象的好处,因为这种协议并没有真正提高抽象级别。此外,如果此类声明出现在多个地方,则必须重复。更糟糕的是,如果这种类型的多个声明是相互关联的(可能会在它们周围传递一个对象),程序会变得脆弱且难以维护,因为以后如果需要更改一个地方的声明,则所有相关的声明都必须更改也被改变了。
解决方案
如果 属性 的用例既涉及协议(例如 ProtocolX
)又涉及 class 的某些方面(例如 ClassX
),则采用以下方法可以考虑:
声明一个附加协议,该协议继承自
ProtocolX
,并添加了ClassX
自动满足的 method/property 要求。像下面的例子,一个方法和一个属性是额外的要求,UIViewController
自动满足。protocol CustomTableViewDelegate: UITableViewDelegate { var navigationController: UINavigationController? { get } func performSegueWithIdentifier(identifier: String, sender: AnyObject?) }
声明一个从
ProtocolX
继承的附加协议,带有ClassX
类型的附加只读 属性。这种方法不仅允许完整地使用ClassX
,而且还展示了不需要实现 subclassClassX
的灵活性。例如: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 e.g. initializer var viewController: UIViewController { return _aViewControllerRef } ... // UITableViewDelegate methods implementation }
PS. 以上代码片段仅供演示,不建议将 UIViewController
和 UITableViewDelegate
混合使用。
编辑 Swift 2+: 感谢@Shaps 的评论,可以添加以下内容以节省必须在所有地方实现所需的 属性。
extension CustomTableViewDelegate where Self: UIViewController {
var viewController: UIViewController { return self }
}