2016-01-14 7 views
10

Ich würde gerne wissen, was das Protokoll entspricht für einen Initialisierer in einer einfachen Klasse, die nur Initialisierungsfunktionalität enthält und nur in einer konkreten Klasse erweitert werden soll.Protokoll Extension Initialisierer

So wahrscheinlich die einfachste ist es, den Code zu erhalten - ich bin für die Protokollerweiterung Äquivalent der folgenden Suche:

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

ich den Code erwartet zu etwa so aussehen:

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

Ich habe Lösungen gesehen, die in anderen StackOverflow-Fragen vorgeschlagen wurden (zB How to define initializers in a protocol extension?), aber ich bin mir nicht sicher, ob sie das Problem der zusätzlichen Parameter im Klasseninitialisierer behandeln oder spezifisch behandeln.

Antwort

11

Sie müssen eine gültige Kette von init zum Erstellen einer Instanz einer Klasse angeben, die Ihre Optionen für Initialisierer in Protokollen einschränkt.

Da Ihr Protokoll nicht sicher sein kann, dass es alle Mitglieder der Klasse abdeckt, die es verwendet, muss jeder Initialisierer, den Sie in Ihrem Protokoll deklarieren, die Initialisierung der "unbekannten" Mitglieder der Klasse an einen anderen Initialisierer delegieren Klasse selbst.

Ich habe Ihr Beispiel angepasst, um dies anhand eines Basisinitials() als Delegationsinitialisierer für das Protokoll zu veranschaulichen.

Wie Sie sehen, erfordert dies, dass Ihre Klasse beim Aufruf von init() Initialwerte für alle Member implementiert. In diesem Fall habe ich das getan, indem ich Standardwerte in der Deklaration jedes Mitglieds angegeben habe. Und da es für einige Mitglieder nicht immer einen tatsächlichen Anfangswert gibt, habe ich sie in automatisch auspackende Optionen geändert.

Und um das interessanter zu machen, kann Ihre Klasse die Initialisierung nicht an einen vom Protokoll bereitgestellten Initialisierer delegieren, es sei denn, dies geschieht über einen Initialisierer für den Benutzerkomfort.

Ich frage mich, ob all diese Einschränkungen die Mühe wert sind. Ich vermute, dass Sie versuchen, ein Protokoll zu verwenden, weil Sie eine Reihe von allgemeinen Variablen benötigen, die konsistent zwischen Klassen, die das Protokoll implementieren, initialisiert werden. Vielleicht würde die Verwendung einer Delegiertenklasse eine weniger verworrene Lösung als Protokolle bieten (nur ein Gedanke).

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

Übereinstimmung mit AnyObject uns redundant, implizit unverpackten Namen und Farbe ist Gefahr und nicht erforderlich (setzen Sie stattdessen die Standardwerte) – user3441734

+0

Vielen Dank für Ihre aufschlussreichen Kommentare, Sie scheinen in ähnlichen Straßenblockaden, die ich hatte. Ich stimme deinem Kommentar zu, ob es sich lohnt. Ich denke, ich suche nach der Best-Practice-Implementierung des obigen Problems in einem protokollorientierten Ansatz. Wenn ich entweder Standardwerte angeben oder sie implizit unverpackt machen möchte, erscheint es mir weder zu praktisch noch zu bewährten Praktiken, und ich frage mich, ob das nach Protokoll riecht, ist nicht der richtige Ansatz für dieses Problem. @ alain-t Können Sie Ihren Kommentar zu einer Delegiertenklasse mit einem Codebeispiel ausfüllen? –

+0

@CraigGrummitt Alle Variablen (und/oder Konstanten) in swift müssen einen gewissen Wert haben, bevor sie verwendet werden (für den Referenztyp, der in Swift als eine Klasse dargestellt wird, ist der Wert eine Referenz, für Werttypen ist es der Wert self). direktes Äquivalent von Null existiert nicht Swift. var i = Optional () hat den Standardwert nil. var j = ImplicitlyUnwrappedOptional () gibt Ihnen das gleiche Ergebnis (beide sind enum 'type') .... (weiter unten) – user3441734

1

Hier ist, was ich für die "Delegierten-Klasse" im Sinn hatte.

Es ist eine Technik, die ich verwende, um gespeicherte Variablen zu einer Klasse mithilfe von Protokollen hinzuzufügen.

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

Nachdem ich meinen Kopf herumgereicht habe, dass dein Protokollname das Wort "Klasse" enthält, folge ich deinem Beispiel, danke für die Eingabe. Um Ihren Vorschlag anders zu formulieren, verwenden Sie eine Factory-Klasse, um alle Eigenschaften aufzubauen, die in meiner ursprünglichen einfachen "Ding" -Klasse waren, oder? Ich schätze, das Problem, das ich als Lösung für diese Frage habe, ist, dass es an seiner Basis wieder eine Klasse benutzt. Aber es ist ein interessantes Beispiel, danke. –

+0

Es verwendet tatsächlich eine Klasse, aber die Vorteile hier sind, dass Sie es außerhalb Ihrer Vererbungshierarchie verwenden können, wie Sie es mit einem Protokoll tun würden. Ich verwende das, um "Mehrfachvererbung" zu implementieren, deshalb habe ich mein Protokoll mit dem Wort Klasse darin benannt.Für eine einzelne Variable sieht es wie ein Overkill aus, aber wenn Sie Daten und Verhalten zu mehreren Klassen hinzufügen möchten, die ihre eigene Hierarchie haben, wird es sinnvoller. Für Ihre speziellen Bedürfnisse stimme ich zu, dass dies wahrscheinlich nicht der beste Ansatz ist. –

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

Awesome, keine Probleme.

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

Nein. Das wird nie funktionieren. Dies bricht viel zu viele Regeln. Das erste und wichtigste ist, dass dies nicht unbedingt alle Eigenschaften festlegt. Betrachten Sie Ihre NamedThing. Was ist name in diesem Fall? Was passiert, wenn der color Setter andere Eigenschaften abruft, die noch nicht festgelegt wurden? Der Compiler kann noch nicht alle möglichen Implementierungen sehen, also hat er keine Ahnung, ob color nur ein Ivar oder etwas Komplizierteres ist. Nein, das wird nicht funktionieren.

Das eigentliche Problem ist "eine abstrakte Klasse, die in einer konkreten Klasse erweitert werden kann." Vergessen Sie Klassen. Vererbung vergessen. Bei Swift geht es um Komposition und Protokolle, nicht um Vererbung.

Also lassen Sie uns über das Beispiel nachdenken, das Sie in den Kommentaren beschreiben (obwohl es in Cocoa auch keine "abstrakten Klassen" gibt). Nehmen wir an, dass das Setzen von Farbe in der Tat viel Code ist, den Sie nicht duplizieren möchten. Das ist kein Problem. Sie brauchen nur eine Funktion.

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

Da der Punkt Ihrer Erweiterung ist Teil Initialisierung zu handhaben, lassen Sie einfach den Teil berechnen Sie benötigen. Versuchen Sie nicht, es zu einem Initialisierer in einer Erweiterung zu machen, da es dann dafür verantwortlich sein müsste, alles zu initialisieren, und das ist sehr schwierig, wenn Sie es mit Vererbung mischen.

+0

Danke für Ihre Antwort Rob. Ich habe meinen Code aus Gründen der Einfachheit auf ein einfaches Beispiel reduziert, ohne darauf einzugehen, warum es eine Klasse sein muss, aber im realen Beispiel ist es eine Klasse, die das Protokoll implementieren muss, da es eine UIKit-Klasse unterklassifiziert . Der Boilerplate-Code sollte durch die Protokollerweiterung dargestellt werden, die die Einstellung der Farbe behandelt. –

+0

Übrigens, ich habe gerade durch Ihren Kommentar realisiert: "Es gibt keine abstrakten Klassen in Swift", dass wir vielleicht von einem anderen Verständnis der "abstrakten Klasse" ausgehen, Entschuldigung, wenn ich das Problem mit einem falschen Verständnis der Begriff. Ich habe die Frage präzisiert. –

+0

Welche UIKit-Klasse ist das, die immer unterklassiert werden muss? (Wir sind uns einig, was eine abstrakte Klasse ist; ich kann einfach nicht an die Klasse denken, von der du sprichst. "UIGestureRecognizer"?) Ich habe den starken Verdacht, dass das eigentliche Problem viel einfacher gelöst werden kann. Das ist das klassische Problem bei der Vereinfachung der Frage. –

-1

Ich bin zu der Schlussfolgerung gekommen, dass die Frage nicht zu beantworten ist, da eine Protokollerweiterung keine Eigenschaften dynamisch definieren kann (es sei denn, sie stellt Standardwerte für Eigenschaften bereit oder deklariert sie als implizit ausgepackt). Um dies zu beheben in einem Protokoll orientiert, es erfordert einen anderen Ansatz, der immer noch beinhaltet deklarieren und alle Variablen in der konkreten Klasse zu initialisieren, so etwas wie:

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

Danke an @ alain-t für die Antwort, die ich Ich werde akzeptieren, da es am ehesten eine Lösung für meine Frage findet, obwohl es implizit unverpackte Eigenschaften enthält.

Vielen Dank an @ rob-napier auch für Ihren Beitrag.