Назначение переменной let в ошибочном инициализаторе swift 1.2

У меня есть структура с ошибочным инициализатором, а не метод экземпляра, а инициализатор. После обновления до 1.2, когда я пытаюсь назначить свойство let внутри инициализатора, я получаю следующую ошибку Cannot assign to 'aspectRatio' in self. Мой код ниже:

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

У меня вопрос, что мне делать, чтобы это исправить?


person thank_you    schedule 10.04.2015    source источник
comment
Разве это не только потому, что у вас есть неизменяемая переменная (с let), и вы назначаете ей дважды (один раз в инициализаторе - 0, а другой раз в конструкторе) - работает ли это, если вы только назначаете ей в конструктор и присвоить 0 в else?   -  person Benjamin Gruenbaum    schedule 10.04.2015
comment
возможный дубликат Swift 1.2, назначающий let после инициализации   -  person ravron    schedule 10.04.2015
comment
@ Бенджамин, теперь я получаю self used before all stored properties are initialized после добавления оператора else и удаления инициализатора.   -  person thank_you    schedule 10.04.2015
comment
@ Райли, в чем разница между этим вопросом и моим? Я использую ошибочный инициализатор, и этот вопрос не стоит.   -  person thank_you    schedule 11.04.2015
comment
Проголосовал за предоставление всего кода, необходимого мне, чтобы просто скопировать и вставить его в приложение, попытаться скомпилировать его и продублировать вашу проблему. Я желаю, чтобы все, кто задал вопрос, сделали это.   -  person matt    schedule 11.04.2015


Ответы (1)


Swift 1.2 закрыл лазейку, связанную со свойствами let:

Новое правило заключается в том, что константа let должна быть инициализирована перед использованием (как var), и что она может быть только инициализирована, но не переназначена или видоизменена после инициализации.

Это правило как раз то, что вы пытаетесь нарушить. aspectRatio — это свойство let, и вы уже присвоили ему значение в его объявлении:

public let aspectRatio: Double = 0

Итак, прежде чем мы доберемся до инициализатора, aspectRatio имеет начальное значение — 0. И это единственное значение, которое он может иметь. Новое правило означает, что вы больше никогда не сможете никогда назначать aspectRatio, даже в инициализаторе.

Решение (и оно всегда было правильным): присвойте ему значение no в его объявлении:

public let aspectRatio: Double

Теперь в инициализаторе либо присвойте ему 0, или присвойте ему w!.doubleValue / h!.doubleValue. Другими словами, позаботьтесь о каждой возможности в инициализаторе, один раз. Это будет единственный раз, так или иначе, когда вы сможете присвоить aspectRatio значение.

Если вы подумаете об этом, вы поймете, что это гораздо более разумный и последовательный подход; раньше вы как бы уклонялись от значения let, и новое правило правильно остановило вас от этого.


В вашем переписанном коде вы не можете инициализировать все свойства в ситуации, когда вы собираетесь выйти из строя и вернуть nil. Я знаю, это может показаться нелогичным, но вы не можете этого сделать. Вы должны инициализировать все свойства, даже если вы собираетесь выйти из системы. Я очень четко объясняю это в своей книге:

Неудачный инициализатор класса не может сказать return nil до тех пор, пока не выполнит все свои обязанности по инициализации. Таким образом, например, неудачный назначенный инициализатор подкласса должен следить за тем, чтобы все свойства подкласса были инициализированы, и должен вызвать super.init(...), прежде чем он сможет сказать return nil. (Здесь есть определенная забавная ирония: прежде чем он сможет разорвать экземпляр, инициализатор должен закончить создание экземпляра.)

EDIT: обратите внимание, что, начиная с Swift 2.2, это требование будет снято. Будет разрешено return nil перед инициализацией свойств. Это поставит инициализаторы классов в один ряд с инициализаторами структур, где это уже было разрешено.

person matt    schedule 10.04.2015
comment
Это имеет смысл для меня, однако я все еще получаю эту ошибку. self used before all stored properties are initialized. Правильно ли я предполагаю, что это совершенно другая проблема? Спасибо за помощь. - person thank_you; 11.04.2015
comment
@ jason328 Проблема в том, что я не знаю, как выглядит ваш новый код. Я объяснил проблему с кодом, который вы показали в своем вопросе. Я не знаю, какое изменение вы внесли в него, что вызывает у вас эту другую проблему. - person matt; 11.04.2015
comment
Мой извиняется. Вопрос теперь обновлен, чтобы отразить самый последний код. - person thank_you; 11.04.2015
comment
С новым кодом вы не позаботились обо всех возможностях, как в инициализаторе. Вы делаете некоторые вещи в условиях if let urlString = ... и if let url = .... Но что, если все это неправда? Тогда этих условий нет, этот код никогда не выполняется — и url и aspectRatio никогда не инициализируются. Но вы должны инициализировать их! Именно об этом говорит вам сообщение об ошибке. Вы должны охватить все возможности. - person matt; 11.04.2015
comment
О, я кое-что упустил. Вы должны сделать это, даже если вы собираетесь вернуть nil, чтобы отметить ошибку. Звучит дико, но это единственный способ обеспечить идеальную согласованность. - person matt; 11.04.2015
comment
Спасибо за подробное объяснение, не давая мне прямого ответа. Простое копирование и вставка не поможет мне понять, почему возникает эта ошибка, и вы отлично справились с объяснением этой проблемы. - person thank_you; 11.04.2015