@property (strong, nonatomic) UIViewController<UITableViewDelegate> *thing;
I want to implement a property like in this Objective-C code in Swift. So here is what I've tried:
我想在这个Objective-C代码中用Swift实现一个属性。这就是我所尝试的:
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. See my updated answer.
Swift 4最终解决了这个问题。看到我的更新答案。
4 个解决方案
#1
11
Update for Swift 4
更新迅速4
Swift 4 has added support for representing a type as a class that conforms to a protocol. The syntax is Class & Protocol
. Here is some example code using this concept from "What's New in Swift" (session 402 from WWDC 2017):
Swift 4增加了对将类型表示为符合协议的类的支持。语法是类和协议。以下是一些使用“Swift新事物”概念的示例代码(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()
}
}
As of Swift 3, this method causes problems because you can't pass in the correct types. If you try to pass in [UIControl]
, it doesn't have the shake
method. If you try to pass in [UIButton]
, then the code compiles, but you can't pass in any UISlider
s. If you pass in [Shakeable]
, then you can't check control.state
, because Shakeable
doesn't have that. Swift 4 finally addressed the topic.
对于Swift 3,这种方法会导致问题,因为您无法传递正确的类型。如果你试图通过[UIControl],它没有震动方法。如果您尝试传入[UIButton],那么代码将被编译,但是您不能传入任何UISliders。如果你通过[Shakeable],那么你就不能检查控制。状态,因为可震动没有。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.
我已经发布了这个答案,以防其他人遇到这个问题,我的解决方案会帮助他们,但我对这个解决方案并不满意。如果有人提出更好的解决方案,我将接受他们的回答。
#2
5
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
}
}
#3
5
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. 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.
在重新考虑这个问题之后,我发现不能用于完全指定类型(换句话说,必须附带附加类型信息,如类类型)的协议不太可能是完整的。此外,尽管Objc风格的ClassType< protocol type >声明非常方便,但它忽略了协议提供的抽象好处,因为这样的协议并没有真正提高抽象级别。此外,如果该声明出现在多个地方,则必须复制。更糟糕的是,如果这种类型的多个声明是相互关联的(可能会传递一个对象给它们),那么程序就会变得脆弱和难以维护,因为如果以后需要更改某个地方的声明,那么所有相关的声明也都必须更改。
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:
如果属性的用例同时涉及协议(例如protocol x)和类的某些方面(例如ClassX),可以考虑以下方法:
-
Declare an additional protocol that inherits from
ProtocolX
with the added method/property requirements whichClassX
automatically satisfy. Like the example below, a method and a property are the additional requirements, both of whichUIViewController
automatically satisfy.声明一个附加的协议,该协议继承自protocol x,附加了ClassX自动满足的方法/属性需求。就像下面的例子一样,一个方法和一个属性是附加的需求,这两者都是UIViewController自动满足的。
protocol CustomTableViewDelegate: UITableViewDelegate { var navigationController: UINavigationController? { get } func performSegueWithIdentifier(identifier: String, sender: AnyObject?) }
-
Declare an additional protocol that inherits from
ProtocolX
with an additional read-only property of the typeClassX
. Not only does this approach allow the use ofClassX
in its entirety, but also exhibits the flexibility of not requiring an implementation to subclassClassX
. For example:声明一个附加的协议,该协议从协议x继承了类型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 }
PS. The snippet above are for demonstration only, mixing UIViewController
and UITableViewDelegate
together is not recommended.
上面的代码片段仅用于演示,不建议将UIViewController和UITableViewDelegate混合在一起。
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 }
}
#4
0
you can declare a delegate in Swift like this:
您可以这样快速声明一个委托:
weak var delegate : UITableViewDelegate?
It will work with even hybrid(Objective-c and swift) project. Delegate should be optional & weak because its availability is not guaranteed and weak does not create retain cycle.
它将与混合(Objective-c和swift)项目合作。委托应该是可选的和弱的,因为它的可用性没有保证,而且弱不能创建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
}
}
#1
11
Update for Swift 4
更新迅速4
Swift 4 has added support for representing a type as a class that conforms to a protocol. The syntax is Class & Protocol
. Here is some example code using this concept from "What's New in Swift" (session 402 from WWDC 2017):
Swift 4增加了对将类型表示为符合协议的类的支持。语法是类和协议。以下是一些使用“Swift新事物”概念的示例代码(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()
}
}
As of Swift 3, this method causes problems because you can't pass in the correct types. If you try to pass in [UIControl]
, it doesn't have the shake
method. If you try to pass in [UIButton]
, then the code compiles, but you can't pass in any UISlider
s. If you pass in [Shakeable]
, then you can't check control.state
, because Shakeable
doesn't have that. Swift 4 finally addressed the topic.
对于Swift 3,这种方法会导致问题,因为您无法传递正确的类型。如果你试图通过[UIControl],它没有震动方法。如果您尝试传入[UIButton],那么代码将被编译,但是您不能传入任何UISliders。如果你通过[Shakeable],那么你就不能检查控制。状态,因为可震动没有。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.
我已经发布了这个答案,以防其他人遇到这个问题,我的解决方案会帮助他们,但我对这个解决方案并不满意。如果有人提出更好的解决方案,我将接受他们的回答。
#2
5
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
}
}
#3
5
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. 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.
在重新考虑这个问题之后,我发现不能用于完全指定类型(换句话说,必须附带附加类型信息,如类类型)的协议不太可能是完整的。此外,尽管Objc风格的ClassType< protocol type >声明非常方便,但它忽略了协议提供的抽象好处,因为这样的协议并没有真正提高抽象级别。此外,如果该声明出现在多个地方,则必须复制。更糟糕的是,如果这种类型的多个声明是相互关联的(可能会传递一个对象给它们),那么程序就会变得脆弱和难以维护,因为如果以后需要更改某个地方的声明,那么所有相关的声明也都必须更改。
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:
如果属性的用例同时涉及协议(例如protocol x)和类的某些方面(例如ClassX),可以考虑以下方法:
-
Declare an additional protocol that inherits from
ProtocolX
with the added method/property requirements whichClassX
automatically satisfy. Like the example below, a method and a property are the additional requirements, both of whichUIViewController
automatically satisfy.声明一个附加的协议,该协议继承自protocol x,附加了ClassX自动满足的方法/属性需求。就像下面的例子一样,一个方法和一个属性是附加的需求,这两者都是UIViewController自动满足的。
protocol CustomTableViewDelegate: UITableViewDelegate { var navigationController: UINavigationController? { get } func performSegueWithIdentifier(identifier: String, sender: AnyObject?) }
-
Declare an additional protocol that inherits from
ProtocolX
with an additional read-only property of the typeClassX
. Not only does this approach allow the use ofClassX
in its entirety, but also exhibits the flexibility of not requiring an implementation to subclassClassX
. For example:声明一个附加的协议,该协议从协议x继承了类型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 }
PS. The snippet above are for demonstration only, mixing UIViewController
and UITableViewDelegate
together is not recommended.
上面的代码片段仅用于演示,不建议将UIViewController和UITableViewDelegate混合在一起。
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 }
}
#4
0
you can declare a delegate in Swift like this:
您可以这样快速声明一个委托:
weak var delegate : UITableViewDelegate?
It will work with even hybrid(Objective-c and swift) project. Delegate should be optional & weak because its availability is not guaranteed and weak does not create retain cycle.
它将与混合(Objective-c和swift)项目合作。委托应该是可选的和弱的,因为它的可用性没有保证,而且弱不能创建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
}
}