@property (strong, nonatomic) UIViewController<UITableViewDelegate> *thing;
このObjective-CコードのようなプロパティをSwiftに実装したいと思います。だからここに私が試したものがあります:
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では、プロトコルに準拠するクラスとして型を表すためのサポートが追加されました。構文は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.isEnabled {
control.shake()
}
}
Swift 3以降、このメソッドは正しい型を渡すことができないため問題を引き起こします。[UIControl]
を渡そうとすると、shake
メソッド。[UIButton]
を渡そうとすると、コードはコンパイルされますが、UISlider
sを渡すことができません。[Shakeable]
を渡すと、 Shakeable
にはcontrol.state
がないため、チェックしないでくださいSwift 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
に実装し(私はしたくない)、サブクラスでそれらをオーバーライドする必要があります。
他の誰かがこの問題に遭遇し、私の解決策が彼らを助ける場合に備えて、私はこの回答を投稿しましたが、解決策に満足していません。誰かがより良い解決策を投稿したら、私は彼らの答えを受け入れます。
これは理想的なソリューションではありませんが、次のように、ジェネリッククラスの代わりにジェネリック関数を使用できます。
class AClass: UIViewController {
@IBOutlet weak var anotherThing: UILabel!
private var thing: UIViewController?
func setThing<T: UIViewController where T: UITableViewDelegate>(delegate: T) {
thing = delegate
}
}
私は同じ問題に遭遇し、一般的なアプローチも試しました。結局、一般的なアプローチは設計全体を壊しました。
この問題について再考した結果、タイプを完全に指定するために使用できないプロトコル(つまり、クラスタイプなどの追加のタイプ情報が必要になるプロトコル)は完全なプロトコルではない可能性があることがわかりました。さらに、宣言するObjcスタイルがClassType<ProtocolType>
は便利です。そのようなプロトコルは実際には抽象化レベルを上げないため、プロトコルによって提供される抽象化の利点を無視します。さらに、そのような宣言が複数の場所に現れる場合は、それを複製する必要があります。さらに悪いことに、そのようなタイプの複数の宣言が相互に関連している場合(おそらく単一のオブジェクトがそれらの周りに渡される)、プログラムは壊れやすくなり、後で1か所の宣言を変更する必要がある場合、関連するすべての宣言が同様に変更されます。
ソリューション
プロパティの使用例にプロトコル(たとえばProtocolX
)とクラスのいくつかの側面(たとえばClassX
)の両方が含まれる場合、次のアプローチを考慮することができます。
ProtocolX
から継承する追加のプロトコルを宣言し、ClassX
が自動的に満たすメソッド/プロパティ要件を追加します。以下の例のように、メソッドとプロパティは追加の要件であり、どちらもUIViewController
が自動的に満たします。
protocol CustomTableViewDelegate: UITableViewDelegate {
var navigationController: UINavigationController? { get }
func performSegueWithIdentifier(identifier: String, sender: AnyObject?)
}
タイプ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
}
PS。上記のスニペットはデモンストレーションのみを目的としており、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にはジェネリックがなく、@ IBOutletプロパティを追加できないため、このエラーが発生します。
編集:1.デリゲートに型を強制する
デリゲートが常にUIViewControllerであることを強制するには、カスタムセッターを実装し、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
}
}