2016-01-14 7 views
10

初期化機能のみを含む単純なクラスの初期化子に相当するプロトコルを知りたいのですが、具体的なクラスで拡張することを意図しています。Protocol Extension Initializer

だから、おそらく最も簡単なコードを表示することです - 私は、次のプロトコル拡張と同等を探しています:

import UIKit 

class Thing { 
    var color:UIColor 
    init(color:UIColor) { 
     self.color = color 
    } 
} 
class NamedThing:Thing { 
    var name:String 
    init(name:String,color:UIColor) { 
     self.name = name 
     super.init(color:color) 
    } 
} 
var namedThing = NamedThing(name: "thing", color: UIColor.blueColor()) 

私のようなものを見て、コードを期待していた。

protocol Thing { 
    var color:UIColor {get set} 
} 
extension Thing { 
    init(color:UIColor) { 
     self.color = color 
    } 
} 
class NamedThing:Thing { 
    var name:String 
    var color:UIColor 
    init(name:String,color:UIColor) { 
     self.name = name 
     self.init(color:color) 
    } 
} 

私は他のStackOverflowの質問(例えばHow to define initializers in a protocol extension?)で提案されている解決策を見たことがありますが、クラス初期化子の追加パラメータのこの問題を解決したり、具体的には対応していません。

答えて

11

クラスのインスタンスを作成するために有効な一連のinitを提供する必要があり、プロトコルの初期化子のオプションが制限されます。

あなたのプロトコルがそれを使用するクラスのすべてのメンバーをカバーすることは確実ではないため、プロトコルで宣言した初期化子は、クラスの「未知の」メンバーの初期化をクラスそのもの。

基本的なinit()をプロトコルの委譲初期化子として使用して例を調整しました。

このように、init()が呼び出されたときにクラスがすべてのメンバーの初期値を実装する必要があります。この場合、私は各メンバーの宣言にデフォルト値を提供することでそれを行いました。また、一部のメンバーには必ずしも実際の初期値があるとは限りませんので、私はそれらをauto-unwrapオプションに変更しました。

さらに面白いと思うが、あなたのクラスは、便利な初期化子を使用しない限り、初期化をプロトコル提供の初期化子に委譲できません。

これらの制限がすべて問題になるかどうかは疑問です。プロトコルを実装するクラス間で一貫して初期化される共通の変数が必要なので、プロトコルを使用しようとしていると思われます。たぶんデリゲートクラスを使用すると、プロトコル(ちょうど思考)よりも複雑ではないソリューションが提供されます。

protocol Thing:AnyObject 
{ 
    var color:UIColor! { get set } 
    init() 
} 

extension Thing 
{  
    init(color:UIColor) 
    { 
     self.init() 
     self.color = color 
    } 
} 

class NamedThing:Thing 
{ 
    var name:String! = nil 
    var color:UIColor! = nil 

    required init() {} 

    convenience init(name:String,color:UIColor) 
    { 
     self.init(color:color) 
     self.name = name 
    } 
} 
+1

AnyObjectに準拠冗長で暗黙のうちにアンラップされた名前と色は危険であり、必須ではありません(デフォルト値を設定します) – user3441734

+0

あなたの洞察力のあるコメントをお寄せいただきありがとうございます。問題の価値があるかどうか私はあなたのコメントに同意します。私はプロトコル指向のアプローチで上記の問題のベストプラクティス実装を探していると思います。 varsのデフォルト値を与えるか暗黙的にアンラップする必要がありますが、これはあまり便利でもベストプラクティスでもないようで、プロトコルを使用するような臭いがこの問題の正しい方法ではないかと思います。 @ alain-tコード例であなたのコメントを「代理人クラス」に記入できますか? –

+0

@CraigGrummitt swift内のすべての変数(および/または定数)は、使用する前に値を持たなければなりません(参照型の場合、Swiftではクラス、値は参照、値型ではself)。 Swiftはnullの直接の等価物が存在しません。 var i =任意()にはデフォルト値nilがあります。 VaRのJ = ImplicitlyUnwrappedOptional は() – user3441734

1

ここで私が "デリゲートクラス"について覚えていたことは次のとおりです。

これは、プロトコルを使用してクラスに格納された変数を追加するために使用する手法です。恐ろしい

class ManagedColors 
{ 
    var color:UIColor 
    // other related variables that need a common initialisation 
    // ... 
    init(color:UIColor) 
    { 
     self.color = color 
     // common initialisations for the other variables 
    } 
} 

protocol ManagedColorClass 
{ 
    var managedColors:ManagedColors { get } 
} 

extension ManagedColorClass 
{  
    // makes properties of the delegate class accessible as if they belonged to the 
    // class that uses the protocol 
    var color:UIColor { 
         get { return managedColors.color } 
         set { managedColors.color = newValue } 
         }  
} 


// NamedThing objects will be able to use .color as if it had been 
// declared as a variable of the class 
// 
// if you add more properties to ManagedColors (and the ManagedColorHost protocol) 
// all your classes will inherit the properties as if you had inherited from them through a superclass 
// 
// This is an indirect way to achive multiple inheritance, or add additional STORED variables with 
// a protocol 
// 
class NamedThing:ManagedColorClass 
{ 
    var name:String 
    var managedColors:ManagedColors 

    init(name:String,color:UIColor) 
    { 
     managedColors = ManagedColors(color:color) 
     self.name = name 
    } 
} 

let red = NamedThing(name:"red", color:UIColor.redColor()) 
print(" \(red.name) \(red.color)") 
+0

あなたのプロトコル名の中に「class」という言葉があることを頭に浮かべた後、あなたの例に従います。あなたの提案を別の方法で表現するために、私のオリジナルの単純な 'Thing'クラスのプロパティを構築するために、ファクトリクラスを使用していますか?私はこの問題の解決策として、私が問題を抱えていると思うのは、その基盤でクラスを再び使用しているということです。しかし、面白い例です、ありがとう。 –

+0

実際にはクラスを使用しますが、ここでの利点は、プロトコルを使用する場合と同じように、継承階層外で使用できることです。私はこれを使って "多重継承"を実装しています。そのため、私のプロトコルにはクラスという名前が付けられています。1つの変数については、過剰なように見えますが、独自の階層を持つ複数のクラスにデータやビヘイビアを追加する場合は、より意味をなさないようになります。あなたの特定のニーズに対しては、おそらく最良のアプローチではないと私は同意します。 –

10
protocol Thing { 
    var color: UIColor {get set} 
} 

、問題なし。

extension Thing { 
    init(color: UIColor) { 
     self.color = color 
    } 
} 

いいえ、これは決してうまくいかないでしょう。これはあまりにも多くのルールを破ります。最初の重要な点は、必ずしもすべてのプロパティを設定する必要はないということです。 NamedThingをご検討ください。この場合、nameとは何ですか? colorセッターがまだ設定されていない他のプロパティをフェッチするとどうなりますか?コンパイラはこれのすべての可能な実装をまだ見ることができないので、colorがちょうどivarかそれとももっと複雑なものかは分かりません。いいえ、これはうまくいかないでしょう。

本当の問題は、「具体的なクラスに拡張することができる抽象クラス」です。クラスを忘れる。継承を忘れる。 Swiftは、継承ではなく、構成とプロトコルに関するものです。

それでは、あなたがコメントで記述例について考えてみましょう(ココアでも、何の「抽象クラスは、」どちらかが存在しません)。実際に色を設定することは、複製したくないコードが多いとしましょう。それは問題ありません。あなたは機能が必要です。あなたの拡張機能のポイントは部分初期化を処理することですので

import UIKit 

protocol Thing { 
    var color: UIColor {get set} 
} 

private extension Thing { 
    static func colorForColor(color: UIColor) -> UIColor { 
     // We don't really use the color directly. We have some complicated code that we don't want to repeat 
     return color 
    } 
} 

final class NamedThing: Thing { 
    var name: String 
    var color: UIColor 

    init(name: String, color: UIColor) { 
     self.name = name 
     self.color = NamedThing.colorForColor(color) 
    } 
} 

、ちょうどそれはあなたが必要な部分を計算してみましょう。エクステンション内でイニシャライザを作成しようとしないでください。すべてを初期化する責任を負う必要があります。継承と混合すると正しく実行するのは非常に難しくなります。

+0

あなたのanwer Robに感謝します。なぜかクラスにする必要はなく、単純化のために私のコードを裸の骨の例に減らしましたが、実際の例では、UIKitクラスをサブクラス化するときにプロトコルを実装する必要があるクラスです。 ボイラープレートコードは、色の設定を処理するプロトコル拡張によって説明されることを意図していました。 –

+0

ちなみに、私はあなたのコメントで "Swiftには抽象クラスはありません"と認識しています。私が "抽象クラス"の異なる理解から取り組んでいるかもしれない、謝罪します。期間。私は質問をより明確に編集しました。 –

+0

いつもサブクラス化されなければならないUIKitクラスは何ですか? (私たちは、抽象クラスが何であるかに同意します、あなたが話しているクラスについて考えることはできません。 "UIGestureRecognizer?)私が強く思っているのは、あなたが直面している実際の問題は、それは質問を過度に単純化する古典的な問題です。 –

-1

プロトコル拡張は、(それがプロパティのデフォルト値を提供しない限り、または開封されたとして、暗黙的にそれらを宣言しています)、動的にプロパティを定義することができないように私は、質問が一種のunanswerableであるという結論になってきました。私は

import UIKit 
protocol Colorable { 
    var color: UIColor {get set} 
} 
protocol Nameable { 
    var name: String {get set} 
} 
class ColoredNamedThing: Colorable, Nameable { 
    var name: String 
    var color: UIColor 

    init(name: String, color: UIColor) { 
     self.name = name 
     self.color = color 
    } 
} 

var coloredNamedThing = ColoredNamedThing(name: "Name", color: UIColor.redColor()) 

答えを@のアラン・トンに感謝します:より多くのプロトコル指向のやり方でこれを解決するには、そのようなものは、まだ具体的なクラス内のすべての変数を宣言して初期化する必要れ、異なるアプローチが必要暗黙のうちにラップされていないプロパティを含むにもかかわらず、私の質問に対する解決法を最もよく見つけるので、受け入れるつもりです。

はまたあなたの貢献のためにも、ロブ・ネーピア@に感謝します。