Swift中的NSCalendar - init可以返回nil,但不是可选的

时间:2014-08-13 10:48:53

标签: swift nscalendar

如果NSCalendar(calendarIdentifier: calendarName)无效,

nil可以返回calendarName - 这是原始的Objective-C行为,在Swift中也是如此。但是,看起来编译器认为初始值设定项返回NSCalendar而不是NSCalendar?,如下所示:

let c1 = NSCalendar(calendarIdentifier: "gregorian")// _NSCopyOnWriteCalendarWrapper
let c2 = NSCalendar(calendarIdentifier: "buddhist")// _NSCopyOnWriteCalendarWrapper

//let c3:NSCalendar = NSCalendar(calendarIdentifier: "rubbish") // run-time error
let c3:NSCalendar? = NSCalendar(calendarIdentifier: "rubbish") // nil

因此,如果初始化程序可以返回nil,我的理解是我应该能够

if let c4 = NSCalendar(calendarIdentifier: "rubbish") as? NSCalendar { 
    //error: conditional downcast from 'NSCalendar' to 'NSCalendar' always succeeds
}

但是,这是一个编译时错误,如图所示。

我在这里误解了什么,如何安全地测试命名日历实际存在?

2 个答案:

答案 0 :(得分:2)

即使初始化程序看起来应该返回一个完整的NSCalendar对象,但它看起来好像是一个隐式的可选项(NSCalandar!)。如果您在调试器中查看对象,它将显示为NSCalendar:

(NSCalendar)$ R1 = 0x0000000000000000 {ObjectiveC.NSObject = parent is NULL}

更奇怪的是,在访问nil NSCalendar时,以下代码不会产生任何运行时错误 - 至少对我而言:

    let x = NSCalendar(calendarIdentifier: "asdasda")
    let y = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)
    if (x.isDateInToday(NSDate())) {
        println("x works")
    }

    if (y.isDateInToday(NSDate())) {
        println("y works")
    }
    if (x == nil) {
        println("x not calendar")
    }

    if (y == nil) {
        println("y not calendar")
    }

对我来说,这个输出' y工作'和' x不是日历'。如果使用nil隐式可选,纯粹的Swift会有运行时错误,但pqnet指出在目标c中调用nil指针上的方法不会产生错误,它只是被忽略 - 这似乎是这样做。很奇怪,但在这种情况下确实可以安全地执行此操作:

    let x = NSCalendar(calendarIdentifier: "asdasda")
    if (x != nil) {
        //do what you need with the calendar
    }

然而,更快捷的方式是使用显式可选 - 即执行此操作:

    let x : NSCalendar? = NSCalendar(calendarIdentifier: "asdasda")
    if let cal = x {
        //do what you need with the calendar as cal
    }

你也可以使用一个隐式的可选let x : NSCalendar =...,并按上面的if x != nil检查 - 你的选择,有时似乎不同的上下文导致隐式或显式的选项......

编辑:

来自Xcode release notes

Swift不支持通过返回null而失败的对象初始值设定项。 (16480364)! 解决方法:如果有工厂方法,请改用它。否则,在可选项中捕获结果。例如:

let url: NSURL? = NSURL(string: "not a url")

答案 1 :(得分:1)

注意:以下内容仅适用于Swift 1.0。在Swift 1.1中,有可用的初始化器。


这有效:

if let c4 = NSCalendar(calendarIdentifier: "rubbish") as NSCalendar? { 

}

在Swift 1.0中,Xcode发行说明中存在一个已知问题,即Swift不支持返回nil的Objective-C初始值设定项。基本上,正在发生的事情是,根据Swift语言,表达式NSCalendar(...)具有类型NSCalendar,这是一种非可选类型(不能是nil)。但是,这实际上是来自Objective-C的导入初始化程序,它可以返回nil

所以当前发生的是当你调用它并返回nil时,你有一个在运行时为nil的值,但是Swift认为它是一个非可选的NSCalendar(不能是nil)。这是一个非常糟糕的情况,你有一个类型不可能的值。 Xcode发行说明提到了一种“解决方法”,您可以在使用它之前将初始化程序的结果转换为可选类型。这样做的原因是在运行时,可选和非可选对象指针都表示为简单指针,其中nil对象指针是空指针,而非可选对象指针不是空指针。从非可选对象指针转换为可选对象指针的操作是一个简单的赋值(在两种情况下它都是非空指针)。但是在值为nil的情况下,简单赋值将其转换为可选类型的(有效)nil值。所以一切都很开心。

但是如果你没有先把它转换成可选的,那么所有的地狱都会破裂,因为你有一个不应该为那种类型的值。

我们不允许您尝试使用as?NSCalendarNSCalendar转发广播,因为NSCalendarNSCalendar的广告不能失败(理论上)