web-dev-qa-db-ja.com

プロトコル拡張イニシャライザー

初期化機能のみを含み、具象クラスでの拡張のみを目的とした単純なクラスのイニシャライザーと同等のプロトコルが何であるかを知りたいです。

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

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の質問で提案された解決策を見てきました(例: プロトコル拡張でイニシャライザーを定義する方法? )が、それらが機能するかどうか、またはクラスイニシャライザーの追加パラメーターのこの問題に具体的に対処するかどうかはわかりません。

11
Craig Grummitt

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

プロトコルは、それを使用するクラスのすべてのメンバーを確実にカバーできるわけではないため、プロトコルで宣言する初期化子は、クラスの「不明な」メンバーの初期化を、クラス自体が提供する別の初期化子に委任する必要があります。

プロトコルの委任初期化子として基本的なinit()を使用して、これを説明するために例を調整しました。

ご覧のとおり、これには、init()が呼び出されたときに、クラスがすべてのメンバーの初期値を実装する必要があります。この場合、各メンバーの宣言にデフォルト値を指定することでそれを行いました。また、一部のメンバーには実際の初期値が常にあるとは限らないため、オプションの自動アンラップに変更しました。

さらに興味深いことに、クラスは、便利な初期化子を介して行わない限り、プロトコルが提供する初期化子に初期化を委任することはできません。

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

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
    }
}
15
Alain T.
protocol Thing {
    var color: UIColor {get set}
}

素晴らしい、問題ありません。

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

いいえ、それは決してうまくいきません。これはあまりにも多くのルールを破ります。最初のそして最も重要なことは、これが必ずしもすべてのプロパティを設定するわけではないということです。 NamedThingについて考えてみましょう。この場合のnameとは何ですか? colorセッターがまだ設定されていない他のプロパティをフェッチするとどうなりますか?コンパイラは、これの可能なすべての実装をまだ確認できないため、colorが単なるivarなのか、それとも非常に複雑なものなのかはわかりません。いいえ、これは機能しません。

本当の問題は「具象クラスで拡張できる抽象クラス」です。クラスを忘れてください。継承を忘れてください。 Swiftは、継承ではなく、構成とプロトコルがすべてです。

それでは、コメントで説明している例について考えてみましょう(ただし、Cocoaには、「抽象クラス」もありません)。色の設定は、実際には複製したくない多くのコードであると仮定しましょう。それは問題ありません。必要なのは関数だけです。

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)
    }
}

拡張機能のポイントはpartial初期化を処理することなので、必要な部分を計算させてください。拡張機能の初期化子にしようとしないでください。すべての初期化を担当する必要があり、継承と組み合わせると正しく実行するのが非常に困難になります。

14
Rob Napier

これが私が「デリゲートクラス」のために考えていたものです。

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

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)")
1
Alain T.