需要在init中设置Swift类的所有常量

30

我有一个Swift类,其中有一个常量实例变量(现在称为实例常量吗?)。 要将值设置为此常量,我需要调用所需对象的初始化程序并传递自身。 然而,由于我需要先初始化所有值,然后调用super.init(),之后才允许访问self,因此我无法这样做。 那么在这种情况下该怎么办?

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()
    }
}
2个回答

34

适用于Swift 1.2及以上版本的更新

不幸的是,似乎不再可能将bluetoothManager作为常量。从Swift 1.2开始,在初始化器中,常量属性只能分配一次值。这不允许我们通过将其声明为可选项以nil值开始,并在初始化过程中稍后更改它。以下是使用bluetoothManager作为变量的更新版本。


(注意:保留原文中的html标签,不进行解释)
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)
    }
}

原始回答

您可以在这里使用隐式解析可选项(对于bluetoothManager),并在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)
    }
}
因为`bluetoothManager`是可选的,所以在调用`super.init()`时,所有属性都已经初始化完成(`bluetoothManager`会被隐式初始化为`nil`)。但是因为我们知道,在类初始化后,`bluetoothManager`一定会有值,所以我们将其声明为明确解包的选项类型,以避免在使用时进行检查。
更新:
一个属性可以被声明为常量,但仍然可以在初始化程序中更改。只需确保它在初始化完成时具有确定的值即可。这在Swift书的“修改初始化期间的常量属性”章节中有记录。
需要将属性初始化为从尚未完全初始化的对象传递self的调用的情况,在“Unowned References and Implicitly Unwrapped Optional Properties”章节中有描述。

1
请参阅Swift书中的“初始化期间修改常量属性”和“无主引用和隐式解包可选属性”部分。 - eofster
2
老实说,我的实例变量需要在super初始化之前初始化似乎没有多大意义。super对我的实例变量一无所知。那么其中的原理是什么?反过来对我来说更有意义。 - qwerty_so
1
@eofster,你回答的两个部分在当前版本中都无效。 - kelin
同意@kelin的观点,这个答案在Swift 2.0和Xcode 7(beta 4)中已经不再适用。很遗憾,因为这是我处理这个问题的首选方式。我将采用延迟存储属性的方法。 - Aron
@kelin 感谢提醒!已更新答案以反映最新的Swift版本。 - eofster
显示剩余2条评论

1

你可以将bluetoothManager设置为@lazy属性,稍后访问它,例如用于startAdvertising吗?

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

init() { ... }

func start() {

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

}

那是我已经尝试过的。而且它有效。但这就放弃了使用let的好处。 :/ - Michael Ochs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接