Swift 2.0: использование self в вызове метода перед сохраненными свойствами инициализируется

Просто начал смотреть на Свифт в эти выходные. Я создаю id для своего класса, чтобы быстро сравнивать объекты. Я хочу неизменный идентификатор, поэтому должен использовать let.

Использование var и инициализации id для "будет исправлять" использование self в вызове метода до того, как сохраненные свойства будут инициализированы", но затем, конечно, он изменен. Каждый другой вопрос, который я видел похожим на это, касается супер класса/вызова super.init, которого у меня нет. Это очень неприятно, я не знаю, почему это не просто.

class MagicCard {

    let id:String
    let name: String
    let manaCost: Int
    let description: String
    let attack: Int
    let defence: Int

    init (name: String, manaCost: Int, description: String, attack: Int, defence: Int) {
        self.name = name
        self.manaCost = manaCost
        self.description = description
        self.attack = attack
        self.defence = defence
        id = generateRandomID()
    }

    private func generateRandomID() -> String {
        let charSet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
        let charSetArray = Array(charSet.characters)
        var id:String = ""
        for _ in (0..<10) {
            id.append(charSetArray[Int(arc4random()) % charSetArray.count])
        }
        return id
    }
}

func == (lhs: MagicCard, rhs: MagicCard) -> Bool {
    return lhs.id == rhs.id
}

Ответ 1

Метод generateRandomID() не использует и не изменяет никаких свойств экземпляра, поэтому одним из возможных решений могло бы стать тип (класс):

private class func generateRandomID() -> String {
    // ...
    return id
}

и используйте его как

id = MagicCard.generateRandomID()

(как также написано в другом ответе, когда я писал это).

Вы также можете отказаться от метода и использовать "немедленно оцененное закрытие":

id = { () -> String in
    // ...
    return id
}()

Но, если целью свойства id является создание объектов Equatable, а затем он вам вообще не нужен. Классы являются справочными типы и экземпляры можно сравнить с оператором "идентично":

func == (lhs: MagicCard, rhs: MagicCard) -> Bool {
    return lhs === rhs
}

что делает метод id и generateRandomID() устаревшим.

Ответ 2

Горячее исправление, которое я могу предложить, - сделать generateRandomID класс func. Так сделайте так:

private class func generateRandomID() -> String {

И вызывается вот так:

id = MagicCard.generateRandomID()

Ответ 3

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

id = ""
id = generateRandomID() // NOT THE SUGGESTED SOLUTION, see below.

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

Однако ничто в generateRandomID() не зависит от состояния объекта, поэтому оно может также быть функцией класса static, которую вы тогда называете MagicCard.generateRandomID(). Но для того, чтобы сделать это еще больше, концептуально генерация случайного идентификатора не имеет ничего общего с магической картой, поэтому вы могли бы даже сделать ее глобальной функцией или изолировать ее до своего класса...

... и как только мы доберемся до этого, почему бы просто не использовать существующий тип NSUUID для представления вашего идентификатора? Либо измените тип id на NSUUID и инициализируйте его id = NSUUID() для случайного UUID, либо сохраните его String и назначьте как id = NSUUID().UUIDString. UUID уже решают проблему уникальных идентификаторов, поэтому вам не нужно придумывать собственный генератор. =)

Это приводит нас к:

let id: NSUUID = NSUUID()

или

let id: String = NSUUID().UUIDString