简体   繁体   English

符合协议和类的Swift属性

[英]Swift Property that conforms to a Protocol and Class

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

I want to implement a property like in this Objective-C code in Swift. 我想在Swift中实现这个Objective-C代码中的属性。 So here is what I've tried: 所以这就是我尝试过的:

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

This compiles. 这编译。 My problem comes when I add properties from the storyboard. 当我从故事板添加属性时,我的问题出现了。 The @IBOutlet tag generates an compiler error. @IBOutlet标记生成编译器错误。

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

The error: 错误:

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

Am I implementing this right? 我实现这个权利吗? What can I do to fix or get around this error? 我该怎么做才能解决或解决这个错误?

EDIT: 编辑:

Swift 4 finally has a solution for this problem. Swift 4终于有了解决这个问题的方法。 See my updated answer. 看到我更新的答案。

Update for Swift 4 Swift 4的更新

Swift 4 has added support for representing a type as a class that conforms to a protocol. Swift 4增加了对将类型表示为符合协议的类的支持。 The syntax is Class & Protocol . 语法是Class & Protocol Here is some example code using this concept from "What's New in Swift" (session 402 from WWDC 2017): 下面是一些使用“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()
    }
}

As of Swift 3, this method causes problems because you can't pass in the correct types. 从Swift 3开始,此方法会导致问题,因为您无法传递正确的类型。 If you try to pass in [UIControl] , it doesn't have the shake method. 如果你试图传入[UIControl] ,它没有shake方法。 If you try to pass in [UIButton] , then the code compiles, but you can't pass in any UISlider s. 如果您尝试传入[UIButton] ,则代码会编译,但您无法传入任何UISlider If you pass in [Shakeable] , then you can't check control.state , because Shakeable doesn't have that. 如果传入[Shakeable] ,则无法检查control.state ,因为Shakeable没有。 Swift 4 finally addressed the topic. Swift 4终于解决了这个话题。

Old Answer 老答案

I am getting around this problem for the time being with the following code: 我暂时用以下代码解决这个问题:

// 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!
}

This seems hackish to me. 这对我来说似乎很骇人听闻。 If any of the delegate methods were required, then I would have to implement those methods in ConformingClass (which I do NOT want to do) and override them in a subclass. 如果需要任何委托方法,那么我将不得不在ConformingClass实现这些方法(我不想这样做)并在子类中覆盖它们。

I have posted this answer in case anyone else comes across this problem and my solution helps them, but I am not happy with the solution. 我已经发布了这个答案,以防其他人遇到这个问题,我的解决方案帮助他们,但我对解决方案不满意。 If anyone posts a better solution, I will accept their answer. 如果有人发布更好的解决方案,我会接受他们的回答。

It's not the ideal solution, but you can use a generic function instead of a generic class, like this: 它不是理想的解决方案,但您可以使用泛型函数而不是泛型类,如下所示:

class AClass: UIViewController {
  @IBOutlet weak var anotherThing: UILabel!
  private var thing: UIViewController?

  func setThing<T: UIViewController where T: UITableViewDelegate>(delegate: T) {
    thing = delegate
  }
}

I came across the same issue, and also tried the generic approach. 我遇到了同样的问题,并尝试了通用方法。 Eventually the generic approach broke the entire design. 最终,通用方法打破了整个设计。

After re-thinking about this issue, I found that a protocol which cannot be used to fully specify a type (in other words, must come with additional type information such as a class type) is unlikely to be a complete one. 在重新思考这个问题后,我发现一个不能用来完全指定类型的协议(换句话说,必须附带类型信息,如类类型)不太可能是一个完整的协议。 Moreover, although the Objc style of declaring ClassType<ProtocolType> comes handy, it disregards the benefit of abstraction provided by protocol because such protocol does not really raise the abstraction level. 此外,虽然声明ClassType<ProtocolType>的Objc样式很方便,但它忽略了协议提供的抽象的好处,因为这样的协议并没有真正提高抽象级别。 Further, if such declaration appears at multiple places, it has to be duplicated. 此外,如果此类声明出现在多个地方,则必须重复。 Even worse, if multiple declarations of such type are interrelated (possibly a single object will be passed around them ), the programme becomes fragile and hard to maintain because later if the declaration at one place needs to be changed, all the related declarations have to be changed as well. 更糟糕的是,如果这种类型的多个声明是相互关联的(可能会在它们周围传递一个对象),程序就会变得脆弱并且难以维护,因为如果稍后需要更改某个地方的声明,所有相关的声明都必须也改变了。

Solution

If the use case of a property involves both a protocol (say ProtocolX ) and some aspects of a class (say ClassX ), the following approach could be taken into account: 如果属性的用例涉及协议(比如ProtocolX )和类的某些方面(比如ClassX ),可以考虑以下方法:

  1. Declare an additional protocol that inherits from ProtocolX with the added method/property requirements which ClassX automatically satisfy. 声明一个从ProtocolX继承的附加协议,其中添加了ClassX自动满足的方法/属性要求。 Like the example below, a method and a property are the additional requirements, both of which UIViewController automatically satisfy. 与下面的示例类似,方法和属性是UIViewController自动满足的附加要求。

     protocol CustomTableViewDelegate: UITableViewDelegate { var navigationController: UINavigationController? { get } func performSegueWithIdentifier(identifier: String, sender: AnyObject?) } 
  2. Declare an additional protocol that inherits from ProtocolX with an additional read-only property of the type ClassX . 声明一个从ProtocolX继承的附加协议,它具有ClassX类型的附加只读属性。 Not only does this approach allow the use of ClassX in its entirety, but also exhibits the flexibility of not requiring an implementation to subclass ClassX . 这种方法不仅允许完整地使用ClassX ,而且还表现出不需要实现子类ClassX的灵活性。 For example: 例如:

     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. PS。 The snippet above are for demonstration only, mixing UIViewController and UITableViewDelegate together is not recommended. 上面的代码片段仅用于演示,不建议将UIViewControllerUITableViewDelegate混合在一起。

Edit for Swift 2+: Thanks for @Shaps's comment, the following could be added to save having to implement the desired property everywhere. 编辑Swift 2+:感谢@ Shaps的评论,可以添加以下内容以节省必须在任何地方实现所需的属性。

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

you can declare a delegate in Swift like this: 你可以像这样在Swift中声明一个委托:

weak var delegate : UITableViewDelegate?

It will work with even hybrid(Objective-c and swift) project. 它甚至可以与混合(Objective-c和swift)项目一起使用。 Delegate should be optional & weak because its availability is not guaranteed and weak does not create retain cycle. 委托应该是可选的和弱的,因为它的可用性不能保证,而弱则不会产生保留周期。

You are getting that error because there are no generics in Objective-C and it will not allow you to add @IBOutlet property. 您收到该错误是因为Objective-C中没有泛型,并且它不允许您添加@IBOutlet属性。

Edit: 1. Forcing a type on delegate 编辑:1。在委托上强制输入类型

To force that delegate is always a UIViewController you can implement the custom setter and throw exception when its not a UIViewController. 要强制该委托始终是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
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM