简体   繁体   English

Swift中有两种不同类型的nil?

[英]Two different types of nil in Swift?

I have encountered some strange behaviour in the Swift language when working in the REPL (=Read-Eval-Print-Loop) where there seem to be two different types of nil values with different behaviour at runtime : 我在使用REPL(= Read-Eval-Print-Loop)时在Swift语言中遇到了一些奇怪的行为,其中似乎有两种不同类型的nil在运行时具有不同的行为:

For this, I define a function g : 为此,我定义了一个函数g

func g(x:String!) {
    println("start!");
    println((x == "foo") ? "foo" : "not");
}

Then I define two variables: 然后我定义了两个变量:

var x:String
var y:String!

When I call g(x) , it works like Objective-C: 当我调用g(x) ,它就像Objective-C一样:

start!
not

When I call g(y) , I got an error: 当我打电话给g(y) ,我收到一个错误:

start!
fatal error: Can't unwrap Optional.None
Execution interrupted. Enter Swift code to recover and continue.
Enter LLDB commands to investigate (type :help for assistance.)

Note that this error is caught at runtime! 请注意,此错误在运行时被捕获! The function is already started. 该功能已经开始。 You can see this because of the "start!" 你可以看到这个因为“开始!” string in the output. 输出中的字符串。

It seems, that in the first case, the function gets a value of nil, together with a note "this is not a nil value". 看来,在第一种情况下,函数的值为nil,并附有“这不是零值”的注释。 In the second case, it seems to get a nil, with an attached note "this value is nil, please scream and don't just use it." 在第二种情况下,似乎得到一个零,附加说明“这个值是零,请尖叫,不要只使用它。”

Why didn't I get an error in the first case, and an error in the second case? 为什么我在第一种情况下没有出错,在第二种情况下出错? Shouldn't be the behaviour of g(x) and g(y) the same? 不应该是g(x)和g(y)的行为相同吗?

Is this expected behaviour? 这是预期的行为吗? Am I missing something? 我错过了什么吗? Is this a bug in Swift? 这是Swift中的错误吗? A bug in the specification? 规范中的错误? Or a bug in the implementation? 或者实施中的错误? Or just a bug in the REPL? 或者只是REPL中的一个错误? Shouldn't it be impossible to access uninitialized variables? 是不是不可能访问未初始化的变量?


The whole session transcript, for reference... 整个会议记录,供参考......

$ /Applications/Xcode6-Beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift
Welcome to Swift!  Type :help for assistance.
  1> func g(x:String!) {println("start!"); println((x=="foo") ? "foo" : "not");} 
  2> var x:String
x: String = {
  core = {
    _baseAddress = Builtin.RawPointer = 0x0000000000000000
    _countAndFlags = 0
    _owner = None
  }
}
  3> var y:String!
y: String! = nil
  4> g(x)
start!
not
  5> g(y)
start!
fatal error: Can't unwrap Optional.None
Execution interrupted. Enter Swift code to recover and continue.
Enter LLDB commands to investigate (type :help for assistance.)
  6> 

To reproduce the whole session, just type the following code into the terminal: 要重现整个会话,只需在终端中键入以下代码:

/Applications/Xcode6-Beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift
func g(x:String!) {println("start!"); println((x=="foo") ? "foo" : "not");}
var x:String
var y:String!
g(x)
g(y)

(the function is written in one line, because copy&paste functionality is broken in the REPL, at least on my machine. Writing it in one line works, though. That's another story...) (该函数写在一行,因为复制和粘贴功能在REPL中被破坏,至少在我的机器上。但是在一行中编写它是有效的。这是另一个故事...)


March 2015 Update: Apple seems to have fixed this issue. 2015年3月更新: Apple似乎解决了这个问题。 It is no longer possible to declare a normal variable without an initial value: 如果没有初始值,则无法再声明正常变量:

 2> var x:String
repl.swift:2:1: error: variables currently must have an initial value when entered at the top level of the REPL
var x:String
^

REPL is doing some initialization for you automatically, even if you don't want it REPL会自动为您做一些初始化,即使您不想要它

I copied & pasteed your code into the playground and it throws this error to me: 我将您的代码复制并粘贴到操场上,它会向我抛出此错误:

error: variable 'x' used before being initialized
g(x)
  ^
<REPL>:22:5: note: variable defined here
var x:String

Which I think it is the correct result. 我认为这是正确的结果。 The code should not compile. 代码不应该编译。

When I type this into REPL 当我输入REPL时

var a:String

it prints 它打印

a: String = {
  core = {
    _baseAddress = Builtin.RawPointer = 0x0000000000000000
    _countAndFlags = 0
    _owner = None
  }
}

so REPL somehow initialize a into a nil String (which I am not sure is it legal to exist) and rest of the story are explained in other answers 所以REPL以某种方式将a初始化为一个nil String(我不确定它是否合法存在)并且在其他答案中解释了故事的其余部分


Looks like REPL is doing some default initialization for every variable so you can't use uninitialized variable 看起来REPL正在为每个变量进行一些默认初始化,因此您不能使用未初始化的变量

Welcome to Swift!  Type :help for assistance.
  1> var a:String
a: String = {
  core = {
    _baseAddress = Builtin.RawPointer = 0x0000000000000000
    _countAndFlags = 0
    _owner = None
  }
}
  2> var b:Int
b: Int = 0
  3> var c:Int[]
c: Int[] = size=0
  4> var d:Dictionary<Int,Int>
d: Dictionary<Int, Int> = {}

more interesting that it still can initialize non-optional type with nil 更有趣的是它仍然可以使用nil初始化非可选类型

  5> import Foundation
  6> var f:NSObject
f: NSObject = {}
  7> var g:NSNumber
g: NSNumber = {
  Foundation.NSValue = <parent is NULL>

}
  8> print(g)
fatal error: Can't unwrap Optional.None
Execution interrupted. Enter Swift code to recover and continue.

So REPL turn access to uninitialized variable (which should be compile-time error) into runtime error 因此,REPL将对未初始化的变量(应该是编译时错误)的访问转变为运行时错误

Welcome to Swift!  Type :help for assistance.
  1> class Test{ var val:Int; init(v:Int) {val=v} }
  2> var t:Test
t: Test = {
  val = <parent is NULL>

}
  3> t.val
Execution interrupted. Enter Swift code to recover and continue.
Enter LLDB commands to investigate (type :help for assistance.)
  4> t = Test(v:1)
  5> t.val
$R2: Int = 1
  6> 

It is expected behavior. 这是预期的行为。

var y:String! 

(with the exclamation mark) is an "implicitly unwrapped optional", it means that it's an optional, but you don't need to unwrap it (add the !) when you want to access it. (带有感叹号)是一个“隐式解包的可选项”,这意味着它是可选的,但是当你想要访问它时,你不需要打开它(添加!)。 But, like an optional, you'll trigger a runtime exception when you try to access it, if it has no value: 但是,像可选项一样,当您尝试访问它时,如果它没有值,您将触发运行时异常:

From “The Swift Programming Language” - page 78 从“The Swift Programming Language” - 第78页

“If you try to access an implicitly unwrapped optional when it does not contain a value, you will trigger a runtime error. “如果在不包含值时尝试访问隐式展开的可选项,则会触发运行时错误。 The result is exactly the same as if you place an exclamation mark after a normal optional that does not contain a value.” 结果与在不包含值的普通可选项之后放置感叹号完全相同。“

So, the fact that g(y) crashes is correct. 因此,g(y)崩溃的事实是正确的。 What seems strange is that g(x) doesn't crash, but it seems to be a behavior of REPL, that initializes x for you. 看起来很奇怪的是g(x)不会崩溃,但它似乎是REPL的一种行为,它会为你初始化x。

For non optional values, the concept of nil doesn't exists. 对于非可选值,nil的概念不存在。 So, x is not nil, it's something like "uninitialized". 所以,x不是零,它就像“未初始化”。 If you try to put this code in a real project 如果您尝试将此代码放在实际项目中

    func g(testValue:String!) {
        println("start!");
        println((testValue == "foo") ? "foo" : "not");
    }

    var x:String
    var y:String!

    g(x)
    g(y)

It won't compile, you'll receive: 它不会编译,你会收到:

Variable 'x' used before being initialized 在初始化之前使用变量'x'

Is this expected behaviour? 这是预期的行为吗? Am I missing something? 我错过了什么吗? Is this a bug in Swift? 这是Swift中的错误吗?

It looks like it's due to your prior activity in the REPL. 看起来这是由于您之前在REPL中的活动。 Here's what I get when I paste your code into a playground: 这是我将代码粘贴到游乐场时得到的结果:

Playground execution failed: error: code after 'return' will never be executedcode after 'return' will never be executed<REPL>:8:3: error: variable 'x' used before being initialized
g(x)
  ^
<REPL>:6:5: note: variable defined here
var x:String

Basically, it complains that you're using x without first giving it a value. 基本上,它抱怨你使用x而没有先给它一个值。 Looking back at the transcript of your session, however, you do give x a value on line 2: 在你的发言全文回首,但是, x线2的值:

  2> var x:String
x: String = {
  core = {
    _baseAddress = Builtin.RawPointer = 0x0000000000000000
    _countAndFlags = 0
    _owner = None
  }
}

So it's no surprise that the compiler is okay with x . 因此编译器对x没问题也就不足为奇了。 The error that you do get is due to y being declared as an implicitly unwrapped variable. 您获得的错误是由于y被声明为隐式展开的变量。 You set it to nil , try to access it, and get an runtime error that says that the value couldn't be unwrapped. 您将其设置为nil ,尝试访问它,并获得一个运行时错误,指出该值无法解开。 The error there isn't surprising -- that's what's supposed to happen when you try to force unwrap a nil variable. 这里的错误并不令人惊讶 - 当你试图强行打开一个nil变量时,应该会发生这种情况。

var x:String var x:String

has to have a value. 必须有一个价值。 May not be nil. 可能不会是零。

That is the problem. 那就是问题所在。 You should not use an uninitialized variable (it is not optional). 您不应该使用未初始化的变量(它不是可选的)。


var y:String! var y:String!

may be used unintitialized, value can be nil. 可以使用未初始化,值可以为零。 Is is like writing 是喜欢写作

var y:String? var y:String? // BUT please every time I use it put an exclamation mark after the y for me //但是每次我使用它都请在y之后为我添加感叹号

So instead of y! 所以而不是y! you can write y. 你可以写y。 But the value may be nil . 但价值可能是nil Using y when the value is nil -> crash. 当值为nil时使用y - >崩溃。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM