2015-04-10 7 views
6

Ich habe eine Struktur mit einem fehlbaren Initialisierer, keine Instanzmethode, sondern ein Initialisierer. Nach der Aktualisierung auf 1.2, wenn ich versuche, eine let Eigenschaft innerhalb des Initialisierers zuzuweisen, erhalte ich den folgenden Fehler Cannot assign to 'aspectRatio' in self. Mein Code unter:Zuweisen von Variable im fehlbaren Initializer Swift 1.2

import Foundation 

public struct MediaItem 
{ 
public let url: NSURL! 
public let aspectRatio: Double 

public var description: String { return (url.absoluteString ?? "no url") + " (aspect ratio = \(aspectRatio))" } 

// MARK: - Private Implementation 

init?(data: NSDictionary?) { 
    var valid = false 
    if let urlString = data?.valueForKeyPath(TwitterKey.MediaURL) as? NSString { 
     if let url = NSURL(string: urlString as String) { 
      self.url = url 
      let h = data?.valueForKeyPath(TwitterKey.Height) as? NSNumber 
      let w = data?.valueForKeyPath(TwitterKey.Width) as? NSNumber 
      if h != nil && w != nil && h?.doubleValue != 0 { 
       aspectRatio = w!.doubleValue/h!.doubleValue 
       valid = true 
      } 
     } 
    } 
    if !valid { 
     return nil 
    } 
} 

struct TwitterKey { 
    static let MediaURL = "media_url_https" 
    static let Width = "sizes.small.w" 
    static let Height = "sizes.small.h" 
} 
} 

Meine Frage ist, was mache ich, um das zu beheben?

+0

Ist dies nicht nur, weil Sie eine unveränderliche Variable haben (mit 'let') und Sie zuweisen es zweimal (einmal in der Initialisierer - 0, und ein anderes Mal im Konstruktor) - funktioniert es, wenn Sie nur ihm im Konstruktor zuweisen und 0 in einem 'else' zuweisen? –

+0

mögliches Duplikat von [Swift 1.2 Zuweisenlassen nach Initialisierung] (http://stackoverflow.com/questions/29564272/swift-1-2-assigning-let-after-initialization) – ravron

+0

@Benjamin, ich bekomme jetzt 'self wird verwendet, bevor alle gespeicherten Eigenschaften initialisiert werden, nachdem die else-Anweisung hinzugefügt und der Initialisierer entfernt wurde. – jason328

Antwort

9

Swift 1.2 hat eine Lücke geschlossen, die mit let Eigenschaften zu tun:

Die neue Regel ist, dass eine Konstante lassen vor der Verwendung initialisiert werden muss (wie ein var), und dass es nur initialisiert werden kann, nach der Initialisierung nicht neu zugewiesen oder mutiert.

Diese Regel ist genau das, was Sie zu verletzen versuchen. aspectRatio ist eine let Eigenschaft und Sie haben bereits einen Wert in seiner Erklärung gegeben:

public let aspectRatio: Double = 0 

Also, bevor wir jemals Initialisierungsstab bekommen, aspectRatio seinen Anfangswert hat - 0. Und das ist der einzige Wert, der es kann jemals haben. Die neue Regel bedeutet, dass Sie niemals immer wieder aspectRatio zuweisen können, nicht einmal in einem Initialisierer.

Die Lösung ist (und das war immer der richtige Weg): weisen Sie keinen Wert in seiner Erklärung:

public let aspectRatio: Double 

Jetzt, in dem Initialisierer entweder zuweisen es 0 oder assign es w!.doubleValue/h!.doubleValue. Mit anderen Worten, achten Sie auf jede Möglichkeit in der Initialisierung, einmal. Das wird die nur Zeit sein, auf die eine oder andere Weise, die Sie aspectRatio einen Wert zuweisen können.

Wenn Sie darüber nachdenken, werden Sie feststellen, dass dies ein viel vernünftiger und konsequenter Ansatz ist; zuvor haben Sie die Bedeutung von let in Verlegenheit gebracht, und die neue Regel hat Sie daran gehindert, dies zu tun.


In Ihrem Umschreiben des Codes, versagen Sie alle Eigenschaften in der Situation, wo Sie und nil Rückkehr zu retten beabsichtigen, zu initialisieren. Ich weiß, es mag widersprüchlich erscheinen, aber das kannst du nicht tun. Sie müssen alle Eigenschaften initialisieren, selbst wenn Sie beabsichtigen, zu retten. Ich diskutiere dies sehr deutlich in my book:

A failable Klasseninitialisierer nicht return nil bis sagen kann, nachdem er alle seine eigenen Initialisierung Aufgaben abgeschlossen hat. So muss zum Beispiel eine fehlbare Unterklasse, die als Initialisierer bezeichnet wird, dafür sorgen, dass alle Eigenschaften der Unterklasse initialisiert werden, und sie muss super.init(...) anrufen, bevor sie return nil sagen kann. (Es gibt eine gewisse köstliche Ironie hier: bevor es die Instanz herunterreißen kann, muss der Initialisierer fertig sein, die Instanz aufzubauen.

)

EDIT: Bitte beachten Sie, dass ab Swift 2.2, wird diese Anforderung angehoben. Es ist zulässig, return nilvor Initialisierungseigenschaften. Dies wird Klasseninitialisierer mit Struct-Initialisierern vergleichbar machen, wo dies bereits legal war.

+0

Das macht für mich Sinn, allerdings bekomme ich diesen Fehler immer noch. 'self verwendet, bevor alle gespeicherten Eigenschaften initialisiert werden'. Darf ich annehmen, dass dies ein ganz anderes Problem ist? Danke für die Hilfe bis jetzt. – jason328

+0

@ jason328 Das Problem ist, dass ich nicht weiß, wie Ihr _new_ Code aussieht. Ich habe das Problem mit dem Code, den Sie in Ihrer Frage gezeigt haben, erklärt. Ich weiß nicht, welche Veränderung Sie daran gemacht haben, dass Sie dieses andere Problem bekommen. – matt

+0

Mein Apolligizes. Die Frage wird jetzt aktualisiert, um den aktuellsten Code zu berücksichtigen. – jason328

Verwandte Themen