web-dev-qa-db-fra.com

besoin de self pour définir toutes les constantes d'une classe Swift in init

J'ai une classe Swift qui a un ivar constant (sont-ils appelés des constantes d'instance maintenant?). Pour définir la valeur de cette constante, j'ai besoin d'appeler un initialiseur de l'objet souhaité et de se passer . Cependant, je ne suis pas autorisé à le faire car je dois d'abord initialiser toutes les valeurs, puis appeler super.init() et ensuite je suis autorisé à accéder à self. Alors, que faire dans ce cas?

class Broadcaster: NSObject, CBPeripheralManagerDelegate {

    let broadcastID: NSUUID
    let bluetoothManager: CBPeripheralManager

    init(broadcastID: NSUUID) {
        self.broadcastID = broadcastID

        let options: Dictionary<NSString, AnyObject> = [ CBPeripheralManagerOptionShowPowerAlertKey: true ]
        self.bluetoothManager = CBPeripheralManager(delegate: self, queue: nil, options: options) // error: 'self' used before super.init call

        super.init()
    }
}
29
Michael Ochs

MISE À JOUR pour Swift 1.2 et versions ultérieures

Malheureusement, il ne semble plus possible d’avoir bluetoothManager comme constante. À partir de Swift 1.2, dans un initialiseur, les propriétés constantes ne peuvent affecter une valeur qu'une seule fois. Cela ne nous permet pas de commencer par une valeur nil en la déclarant comme facultative et le modifier plus tard dans le processus d'initialisation. Voici la version mise à jour avec bluetoothManager comme variable.

class Broadcaster: NSObject, CBPeripheralManagerDelegate {

    let broadcastID: NSUUID
    var bluetoothManager: CBPeripheralManager!

    init(broadcastID: NSUUID) {
        self.broadcastID = broadcastID
        super.init()
        let options: Dictionary<String, AnyObject> = [ CBPeripheralManagerOptionShowPowerAlertKey: true ]
        self.bluetoothManager = CBPeripheralManager(delegate: self, queue: nil, options: options)
    }
}

Réponse originale

Vous pouvez utiliser ici l'option implicitement déballée (pour bluetoothManager) et lui affecter la valeur après super.init():

class Broadcaster: NSObject, CBPeripheralManagerDelegate {

    let broadcastID: NSUUID
    let bluetoothManager: CBPeripheralManager!

    init(broadcastID: NSUUID) {
        self.broadcastID = broadcastID
        super.init()
        let options: Dictionary<NSString, AnyObject> = [ CBPeripheralManagerOptionShowPowerAlertKey: true ]
        self.bluetoothManager = CBPeripheralManager(delegate: self, queue: nil, options: options)
    }
}

Étant donné que bluetoothManager est facultatif, au moment où super.init() est appelée, toutes les propriétés sont initialisées (bluetoothManager est implicitement initialisée avec nil). Mais comme nous savons que bluetoothManager aura définitivement la valeur après l'initialisation de la classe, nous la déclarons comme explicitement dépliée pour éviter les vérifications lors de son utilisation.

METTRE À JOUR

Une propriété peut être déclarée comme constante et toujours être modifiée dans l'initialiseur. Il suffit de s'assurer qu'il a une valeur définie au moment où l'initialisation se termine. Ceci est documenté dans le chapitre "Modification des propriétés constantes pendant l'initialisation" du livre Swift.

La situation où une propriété doit être initialisée avec un appel où self doit être passé à partir d'un objet non encore complètement initialisé est décrite dans le chapitre "Références sans propriétaire et propriétés facultatives implicitement non enveloppées".

34
eofster

Que diriez-vous de configurer votre bluetoothManager en tant que @lazy propriété et y accéder plus tard, par exemple à startAdvertising?

@lazy var bluetoothManager: CBPeripheralManager = CBPeripheralManager(delegate: self, queue: nil)

init() { ... }

func start() {

    self.bluetoothManager.startAdvertising([ "foo" : "bar" ])

}
1
Karl