簡體   English   中英

Swift中有兩種不同類型的nil?

[英]Two different types of nil in Swift?

我在使用REPL(= Read-Eval-Print-Loop)時在Swift語言中遇到了一些奇怪的行為,其中似乎有兩種不同類型的nil在運行時具有不同的行為:

為此,我定義了一個函數g

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

然后我定義了兩個變量:

var x:String
var y:String!

當我調用g(x) ,它就像Objective-C一樣:

start!
not

當我打電話給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.)

請注意,此錯誤在運行時被捕獲! 該功能已經開始。 你可以看到這個因為“開始!” 輸出中的字符串。

看來,在第一種情況下,函數的值為nil,並附有“這不是零值”的注釋。 在第二種情況下,似乎得到一個零,附加說明“這個值是零,請尖叫,不要只使用它。”

為什么我在第一種情況下沒有出錯,在第二種情況下出錯? 不應該是g(x)和g(y)的行為相同嗎?

這是預期的行為嗎? 我錯過了什么嗎? 這是Swift中的錯誤嗎? 規范中的錯誤? 或者實施中的錯誤? 或者只是REPL中的一個錯誤? 是不是不可能訪問未初始化的變量?


整個會議記錄,供參考......

$ /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> 

要重現整個會話,只需在終端中鍵入以下代碼:

/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)

(該函數寫在一行,因為復制和粘貼功能在REPL中被破壞,至少在我的機器上。但是在一行中編寫它是有效的。這是另一個故事...)


2015年3月更新: Apple似乎解決了這個問題。 如果沒有初始值,則無法再聲明正常變量:

 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會自動為您做一些初始化,即使您不想要它

我將您的代碼復制並粘貼到操場上,它會向我拋出此錯誤:

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

我認為這是正確的結果。 代碼不應該編譯。

當我輸入REPL時

var a:String

它打印

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

所以REPL以某種方式將a初始化為一個nil String(我不確定它是否合法存在)並且在其他答案中解釋了故事的其余部分


看起來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> = {}

更有趣的是它仍然可以使用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.

因此,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> 

這是預期的行為。

var y:String! 

(帶有感嘆號)是一個“隱式解包的可選項”,這意味着它是可選的,但是當你想要訪問它時,你不需要打開它(添加!)。 但是,像可選項一樣,當您嘗試訪問它時,如果它沒有值,您將觸發運行時異常:

從“The Swift Programming Language” - 第78頁

“如果在不包含值時嘗試訪問隱式展開的可選項,則會觸發運行時錯誤。 結果與在不包含值的普通可選項之后放置感嘆號完全相同。“

因此,g(y)崩潰的事實是正確的。 看起來很奇怪的是g(x)不會崩潰,但它似乎是REPL的一種行為,它會為你初始化x。

對於非可選值,nil的概念不存在。 所以,x不是零,它就像“未初始化”。 如果您嘗試將此代碼放在實際項目中

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

    var x:String
    var y:String!

    g(x)
    g(y)

它不會編譯,你會收到:

在初始化之前使用變量'x'

這是預期的行為嗎? 我錯過了什么嗎? 這是Swift中的錯誤嗎?

看起來這是由於您之前在REPL中的活動。 這是我將代碼粘貼到游樂場時得到的結果:

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

基本上,它抱怨你使用x而沒有先給它一個值。 在你的發言全文回首,但是, x線2的值:

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

因此編譯器對x沒問題也就不足為奇了。 您獲得的錯誤是由於y被聲明為隱式展開的變量。 您將其設置為nil ,嘗試訪問它,並獲得一個運行時錯誤,指出該值無法解開。 這里的錯誤並不令人驚訝 - 當你試圖強行打開一個nil變量時,應該會發生這種情況。

var x:String

必須有一個價值。 可能不會是零。

那就是問題所在。 您不應該使用未初始化的變量(它不是可選的)。


var y:String!

可以使用未初始化,值可以為零。 是喜歡寫作

var y:String? //但是每次我使用它都請在y之后為我添加感嘆號

所以而不是y! 你可以寫y。 但價值可能是nil 當值為nil時使用y - >崩潰。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM