Swift-переменная не инициализируется перед использованием (но она не используется)

В настоящее время у меня есть такой быстрый код, как этот:

class C {
   let type: Type;
   var num = 0;
   init() {
       self.type = Type({ (num: Int) -> Void in
           self.num = num;
       });
   }
}

Компилятор Swift отказывается разрешить это, говоря, что я ссылался на self.type до его инициализации, хотя это явно не соответствует действительности. Кроме того, я не могу использовать обходной путь, найденный в других вопросах /ответах, потому что type не является обязательным, и он неизменен, поэтому невозможно инициализировать бессмысленно сначала nil.

Как я могу заставить компилятор Swift принимать этот совершенно правильный код?

Это не имеет отношения к раннему возврату из инициализатора. Обратный вызов выполняется асинхронно - он сохраняется и затем используется позже.

У меня также есть несколько дополнительных let, которые инициализируются после этого. Мне бы пришлось превратить их все в изменяемые опции, даже если они не являются обязательными и не могут быть изменены.

10 голосов | спросил Puppy 11 PM00000040000005831 2015, 16:09:58

4 ответа


0

Это работает:

class C {
    var type: Type?;
    var num = 0;
    init() {
        self.type = Type({ (num: Int) -> Void in
            self.num = num;
        });
    }
}

Я полагаю, вы знали это. Но вы хотите знать, почему ваша версия не работает.

Теперь для сложной части: для строки

self.num = num;

чтобы работать, компилятор должен передать себя внутрь замыкания. Замыкание может быть и, вероятно, выполняется внутри конструктора Type.

Это как если бы вы написали

self.type = Type({ (self: C, num: Int) -> Void in
    self.num = num    
});

, что синтаксически неправильно, но объясняет, что компилятор должен сделать, чтобы скомпилировать ваш код.

Чтобы передать этот необходимый экземпляр self в конструктор Type, self должен быть инициализирован. Но self не инициализируется, потому что вы все еще в конструкторе.

Компилятор сообщает, какая часть self не инициализирована, когда вы пытаетесь передать self в конструктор Type.

P.S.

Очевидно, что Type знает num в вашем коде. Если вы хотите использовать let in C вместо var, вы можете сделать ...

class Type {
    let num: Int
    init () {
        num = 3
    }
}
class C {
    let type: Type;
    var num = 0;
    init() {
        self.type = Type();
        num = type.num
    }
}

или даже

class C {
    let type: Type;
    var num: Int {
        return type.num
    }
    init() {
        self.type = Type();
    }
}

в зависимости от того, хотите ли вы изменить num или нет. Оба примера компилируются без ошибок.

ответил Gerd Castan 11 PM00000050000005031 2015, 17:02:50
0

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

struct A {
    init(_ f: (Int) -> Void) { f(1) }
}

class C {
    let type: A
    var num = 0 {
        didSet { print(type) }
    }
    init() {
        self.type = A({ (num: Int) -> Void in
            self.num = num
        })
    }
}

Если вы пойдете по логике, вы заметите, что self.type доступен через print до его инициализации. Свифт в настоящее время не может доказать, что этого не произойдет, и поэтому не позволяет этого. (Теоретический компилятор Swift может доказать, что этого не произойдет в некоторых особых случаях, но для большинства нетривиального кода он может столкнуться с проблемой остановки. В любом случае, текущий компилятор Swift не достаточно мощный, чтобы сделать это доказательство, и это нетривиальное доказательство.)

Одним из решений, хотя и несколько неудовлетворительным, является использование неявно развернутых опций:

private(set) var type: A! = nil

За исключением объявления, все остальные части кода одинаковы. Вы не должны рассматривать это как дополнительное. На практике это просто отключает проверки «используется перед инициализацией» для этой переменной. К сожалению, это также делает его устанавливаемым внутри текущего файла, но делает его неизменным для всех остальных.

Это техника, которую я чаще всего использовал, хотя часто я пытаюсь переделать систему так, чтобы она не требовала такого закрытия (не всегда возможно, но я часто пытаюсь попробовать). Это не красиво, но это последовательно и ограничивает уродливое.

Еще одна техника, которая может работать в некоторых случаях, - это лень.

class C {
    lazy var type: A = {
        A({ (num: Int) -> Void in self.num = num })}()
    var num = 0
    init() {}
}

Иногда это работает, иногда нет. В вашем случае это может. Когда это работает, это довольно приятно, потому что делает свойство действительно неизменяемым, а не просто публично неизменным, и, конечно, потому что оно не требует !

ответил Rob Napier 11 PM00000050000002631 2015, 17:32:26
0

Интересно.

Похоже, ошибка исчезнет, ​​если вы не будете ссылаться на self внутри замыкания .

Если обратный вызов синхронный, вы можете изменить свой код следующим образом:

class C {
    let type: Type
    var num = 0
    init() {
        var numTemp = 0 // create a temporary local var
        let initialType = Type({ (num: Int) -> () in
            numTemp = num // avoid self in the closure
        });
        self.type = initialType
        self.num = numTemp
    }
}
  

Важно: это НЕ сработает, если замыкание асинхронное.

Протестировано с Xcode (Playground) 6.4 + Swift 1.2

Надеюсь, это поможет.

ответил Luca Angeletti 11 PM00000040000002531 2015, 16:47:25
0

Как сказал appzYourLife, будет достаточно временной переменной для num:

class Type{
    var y: (Int)->Void
    init(y2:((Int)->Void)){
        self.y = y2
    }
}

class C {
    let type: Type
    var num: Int = 0
    init() {
        var num2 = 0 
        self.type = Type(y2: { (num3: Int) -> () in
            num2 = num3
        });
        self.num = num2
    }
}

Однако вам не нужна временная переменная для type, это сообщение об ошибке вводит в заблуждение.

ответил 11 PM00000050000002731 2015, 17:00:27

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132