2015-08-11 4 views
10

В настоящее время у меня есть какой-то быстрый код, как это:Свифта переменная не инициализируется перед использованием (но не используется)

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

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

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

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

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

+0

У вас может быть что-то типа «Тип»? –

+0

Не имеет значения.Любой тип, который выполняет обратный вызов этой сигнатуры в конструкторе.] – Puppy

+0

Вы правы, я удалил его. –

ответ

2

Интересно.

Похоже, что ошибка уходит, если вы избегаете ссылок 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 (площадка) 6,4 + Свифта 1,2

Надежда это помогает.

+0

Закрытие действительно полностью асинхронное. – Puppy

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, это сообщение об ошибке вводит в заблуждение.

10

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

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 конструктору Type.

P.S.

Очевидно, тип знает номер в вашем коде. Если вы хотите использовать впустить C вместо вар вы могли бы сделать ...

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 изменить или нет. Оба примера скомпилированы без ошибок.

+0

Может быть, но определенно нет, так как я написал Type менее чем за пять минут до C. О, и даже если бы это было так, часть «самого себя», к которой обращаются, уже инициализирована. – Puppy

+1

Элемент, который вы используете, инициализируется. Но он должен быть полностью инициализирован для передачи, а это не так. –

+0

В принципе вы можете легко ссылаться на неинициализированные или частично инициализированные вещи. Это просто вопрос, сколько вы должны бить компилятор Swift в лицо, пока он не сдастся и перестанет жаловаться на то, что он безмозглый. – Puppy

4

Во-первых, важно объяснить, почему это не совсем правильный код, и что не совсем ясно, что 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() {} 
} 

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