符合协议和类的Swift属性

时间:2015-05-28 01:38:22

标签: ios swift generics properties protocols

@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终于有了解决这个问题的方法。请参阅我更新的答案。

4 个答案:

答案 0 :(得分:17)

Swift 4的更新

Swift 4增加了对将类型表示为符合协议的类的支持。语法为Class & Protocol。下面是一些使用这个概念的示例代码来自&#34; Swift&#34;中的新功能。 (2017年WWDC会议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没有。// 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! } 。 Swift 4终于解决了这个话题。

旧答案

我暂时使用以下代码解决此问题:

ConformingClass

这对我来说似乎很狡猾。如果需要任何委托方法,那么我将不得不在{{1}}中实现这些方法(我不想这样做)并在子类中覆盖它们。

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

答案 1 :(得分:6)

它不是理想的解决方案,但您可以使用泛型函数而不是泛型类,如下所示:

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

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

答案 2 :(得分:6)

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

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

<强>解决方案

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

  1. 声明一个继承自ProtocolX的附加协议,其中添加了ClassX自动满足的方法/属性要求。与下面的示例一样,方法和属性是附加要求,UIViewController都会自动满足这些要求。

    protocol CustomTableViewDelegate: UITableViewDelegate {
        var navigationController: UINavigationController? { get }
        func performSegueWithIdentifier(identifier: String, sender: AnyObject?)
    }
    
  2. 声明一个从ProtocolX继承的附加协议,其附加属性为ClassX的只读属性。这种方法不仅允许完整地使用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 e.g. initializer
        var viewController: UIViewController { return _aViewControllerRef } 
    
        ... // UITableViewDelegate methods implementation
    }
    
  3. PS。以上代码段仅供演示,不建议将UIViewControllerUITableViewDelegate混合在一起。

    编辑Swift 2 +:感谢@ Shaps的评论,可以添加以下内容以节省必须在任何地方实现所需的属性。

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

答案 3 :(得分:0)

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

weak var delegate : UITableViewDelegate?

它甚至可以与混合(Objective-c和swift)项目一起使用。代表应该是可选的&amp;弱,因为它的可用性不能保证,而弱则不会产生保留周期。

您收到该错误,因为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
    }
}