“叹号”在斯威夫特语中是什么意思?

快速编程语言指南 有以下例子:

class Person {
let name: String
init(name: String) { self.name = name }
var apartment: Apartment?
deinit { println("\(name) is being deinitialized") }
}


class Apartment {
let number: Int
init(number: Int) { self.number = number }
var tenant: Person?
deinit { println("Apartment #\(number) is being deinitialized") }
}


var john: Person?
var number73: Apartment?


john = Person(name: "John Appleseed")
number73 = Apartment(number: 73)


//From Apple's “The Swift Programming Language” guide (https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html)

然后,当把公寓分配给这个人时,他们使用一个叹号来“展开实例”:

john!.apartment = number73

“展开实例”是什么意思?为什么要这样?这与仅仅做下列事情有什么不同:

john.apartment = number73

我刚学斯威夫特语,只是想把基本的东西记下来。


更新:
我所遗漏的最大问题(答案中没有直接说明——至少在写这篇文章的时候没有)是,当你做以下事情的时候:

var john: Person?

这并不意味着“ john是类型 Person,它可能是零”,因为我最初的想法。我只是误解了 PersonPerson?是完全独立的类型。一旦我掌握了这一点,所有其他的 ?!疯狂,以及下面的伟大答案,使得更多的意义。

157536 次浏览

john是一个可选的var,它可以包含一个nil值。为了确保该值不是nil,在var名称后面使用!

从文档

一旦您确定可选选项确实包含一个值,您可以通过在可选选项名称的末尾添加一个感叹号(!)来访问它的底层值。感叹号有效地表示,“我知道这个可选选项肯定有价值;请使用它。”

另一种检查非nil值的方法是(可选的展开)

    if let j = json {
// do something with j
}

John是一个可选的Person,这意味着它可以保存值或为nil。

john.apartment = number73

如果John不是可选选项,则使用。由于john从不为nil,我们可以确定它不会在nil值时调用apartment。而

john!.apartment = number73

向编译器承诺John不是nil,然后打开可选选项以获取John的值并访问John的公寓属性。如果你知道john不是nil就用这个。如果你在可选的nil上调用这个,你会得到一个运行时错误。

文档中包含了一个很好的示例,其中convertedNumber是可选的。

if convertedNumber {
println("\(possibleNumber) has an integer value of \(convertedNumber!)")
} else {
println("\(possibleNumber) could not be converted to an integer")
}

如果john是一个可选的变量(这样声明)

var john: Person?

那么john可能没有值(在ObjC中为nil值)

感叹号基本上告诉编译器“我知道这有一个值,你不需要测试它”。如果你不想使用它,你可以有条件地测试它:

if let otherPerson = john {
otherPerson.apartment = number73
}

只有当john有值时,这个内部才有价值。

“展开实例”是什么意思?为什么有必要?

据我所知(这对我来说也很陌生)……

术语“包装”意味着我们应该把一个可选变量想象成一个礼物,包裹在闪亮的纸里,可能(可悲的!)是空的

当“包装”时,可选变量的值是一个包含两个可能值的enum(有点像布尔值)。这个enum描述变量是否包含一个值(Some(T)),或者是否包含一个值(None)。

如果有值,则可以通过“展开”变量来获得(从Some(T)获得T)。

john!.apartment = number73john.apartment = number73有什么不同?(转述)

如果你写一个可选变量的名字(例如文本john,没有!),这是指“包装”enum (Some/None),而不是值本身(T)。所以john不是Person的实例,它没有apartment成员:

john.apartment
// 'Person?' does not have a member named 'apartment'

实际的Person值可以用多种方式展开:

  • “强制展开”:john!(如果EYZ1值存在,则给出Person值,如果为nil则运行时错误)
  • "optional binding": if let p = john { println(p) }(如果值存在则执行println)
  • "optional chains ": john?.learnAboutSwift()(如果值存在,则执行这个编造的方法)

我猜你会选择其中一种展开方式,这取决于在nil情况下会发生什么,以及它的可能性有多大。这种语言设计强制显式地处理nil情况,我认为这比Obj-C提高了安全性(在Obj-C中很容易忘记处理nil情况)。

# EYZ0:

感叹号也用于声明“隐式未包装的可选选项”的语法中。

在迄今为止的示例中,john变量被声明为var john:Person?,并且它是一个Optional变量。如果需要该变量的实际值,则必须使用上述三种方法之一展开它。

如果它被声明为var john:Person!,变量将是一个隐式未包装的可选变量(参见Apple book中带有此标题的部分)。在访问值时不需要展开这类变量,并且john可以在不使用其他语法的情况下使用。但苹果的书说:

当变量有可能在稍后变为nil时,不应该使用隐式打开的可选选项。如果您需要在变量的生命周期内检查nil值,请始终使用正常的可选类型。

# EYZ0:

Mike Ash的文章“有趣的Swift特性”为可选类型提供了一些动力。我觉得写得很好,很清楚。

# EYZ0:

另一篇关于感叹号使用隐式解包装可选的有用文章是Chris Adamson写的“斯威夫特和最后一英里”。这篇文章解释说,这是苹果公司用于声明他们的Objective-C框架所使用的可能包含nil的类型的实用措施。将类型声明为可选(使用?)或隐式展开(使用!)是“安全性和便利性之间的权衡”。在本文给出的示例中,Apple选择将类型声明为隐式展开,这使得调用代码更方便,但不太安全。

也许Apple会在未来梳理他们的框架,删除隐式展开参数(“可能永远不会为nil”)的不确定性,并将它们替换为可选的(“当然可能是nil,特别是[希望有文档!)或标准的非可选(“永不为空”)声明,这取决于它们的Objective-C代码的确切行为。

在objective C中,没有值的变量等于“nil”(也可以使用与0和false相同的“nil”值),因此可以在条件语句中使用变量(有值的变量与“TRUE”相同,而那些没有值的变量等于“false”)。

Swift通过提供“可选值”来提供类型安全。即,它可以防止因分配不同类型的变量而形成错误。

所以在Swift中,只有条件语句可以提供布尔值。

var hw = "Hello World"

这里,虽然'hw'是一个字符串,但它不能像objective - C那样在if语句中使用。

//This is an error


if hw


{..}

为此,它需要被创建为,

var nhw : String? = "Hello World"


//This is correct


if nhw


{..}

以下是我认为的区别:

var john: Person?

意味着john可以是nil

john?.apartment = number73

编译器将这一行解释为:

if john != nil {
john.apartment = number73
}

john!.apartment = number73

编译器将简单地解释这一行:

john.apartment = number73

因此,使用!将打开if语句,并使其运行得更快,但如果john为nil,则会发生运行时错误。

这里的wrap并不是指它是内存包装,而是指它是代码包装,在这种情况下,它是用if语句包装的,因为苹果非常关注运行时的性能,他们想给你一种方法让你的应用以最好的性能运行。

更新:

4年后回到这个答案,因为我在Stackoverflow中获得了最高的声誉:) 当时我对拆封的含义有一点误解。现在四年过去了,我相信在这里展开的意义是将代码从最初的紧凑形式扩展开来。它还意味着移除对象周围的模糊性,因为根据定义我们不确定它是否为nil。就像艾希礼上面的回答一样,把它想象成一个礼物,里面什么都没有。但我仍然认为展开是代码展开,而不是使用enum.

基于内存的展开

博士TL;

感叹号在Swift语言中是什么意思?

感叹号有效地说,“我知道这是可选的 肯定有一个值;请使用它。”这被称为可选值的强制展开:

例子

let possibleString: String? = "An optional string."
print(possibleString!) // requires an exclamation mark to access its value
// prints "An optional string."


let assumedString: String! = "An implicitly unwrapped optional string."
print(assumedString)  // no exclamation mark is needed to access its value
// prints "An implicitly unwrapped optional string."

来源:# EYZ0

在这种情况下……

John:人!

它意味着,最初John的值为nil,它会被设置,一旦设置,就不会再以nil为先导。因此,为了方便起见,我可以使用更简单的语法来访问可选变量因为这是一个隐式展开的可选变量

!意味着您正在强制打开对象!遵循。更多信息可以在苹果文档中找到,可以在这里找到:https://developer.apple.com/library/ios/documentation/swift/conceptual/Swift_Programming_Language/TheBasics.html

简单地说,感叹号意味着一个可选选项正在被打开。可选变量可以有值,也可以没有值——因此可以使用if let语句如图所示检查变量是否为空,然后强制展开它。如果你强制打开一个空的可选选项,你的程序会崩溃,所以要小心!可选项是通过在变量的显式赋值后面加一个问号来声明的,例如我可以这样写:

var optionalExample: String?

这个变量没有值。如果我要打开它,程序会崩溃Xcode会告诉你你试图打开一个值为nil的可选对象。

希望这有帮助。

!在对象的末尾表示该对象是可选的,如果它可以返回nil则展开。这通常用于捕获错误,否则会使程序崩溃。

下面是一些例子:

var name:String = "Hello World"
var word:String?

其中word是可选值。意味着它可能包含或不包含某个值。

word = name

这里name有一个值,所以我们可以给它赋值

var cow:String = nil
var dog:String!

dog强制打开意味着它必须包含一个值

dog = cow

应用程序将崩溃,因为我们将nil分配给unwrapped

如果你熟悉c#,这就像用问号声明的Nullable类型:

Person? thisPerson;

在这种情况下,感叹号相当于像这样访问可空类型的.Value属性:

thisPerson.Value

一些大局观的观点可以补充到其他有用但更注重细节的答案中:

在Swift中,感叹号出现在几种情况下:

  • 强制拆封:let name = nameLabel!.text
  • 隐式打开可选选项:var logo: UIImageView!
  • 强制施法:logo.image = thing as! UIImage
  • 未处理的异常:try! NSJSONSerialization.JSONObjectWithData(data, [])

它们中的每一个都是不同的语言结构,具有不同的含义,但它们都有三个重要的共同点:

1. 感叹号绕过了Swift的编译时安全检查。

当你在Swift中使用!时,你实际上是在说,“嘿,编译器,我知道你认为错误可以发生在这里,但我完全确定它永远不会发生。”

并不是所有有效的代码都符合Swift的编译时类型系统——或者任何语言的静态类型检查。有些情况下,你可以从逻辑上证明一个错误永远不会发生,但你不能证明它。这就是为什么Swift的设计师一开始就添加了这些功能。

然而,无论何时使用!,您都排除了为错误提供恢复路径,这意味着……

2. 感叹号是潜在的崩溃。

一个感叹号还说,“嘿,斯威夫特,我确定这个错误永远不会发生,你去让我的整个应用崩溃总比我为它编写恢复路径要好。”

这是一个危险的论断。可以是正确的:在关键任务代码中,您已经认真考虑了代码的不变量,伪输出可能比崩溃更糟糕。

然而,当我在野外看到!时,它很少被如此谨慎地使用。相反,它经常意味着,“这个值是可选的,我并没有真正想过为什么它可能是nil或如何正确处理这种情况,但添加!使它编译……所以我的代码是正确的,对吗?”

小心感叹号的傲慢。而不是……

3.感叹号最好少用。

这些!构造中的每一个都有一个?对应的构造,它迫使你处理错误/nil情况:

  • 条件展开:if let name = nameLabel?.text { ... }
  • 可选:# EYZ0
  • 条件类型转换:logo.image = thing as? UIImage
  • 失败时为空异常:try? NSJSONSerialization.JSONObjectWithData(data, [])

如果您想要使用!,那么最好仔细考虑为什么不使用?。如果!操作失败,使程序崩溃真的是最好的选择吗?# EYZ3

是否有一个合理的恢复路径,你的代码可以采取在nil/错误的情况下?如果是,编码它。

如果它不可能是nil,如果错误永远不会发生,那么有没有一种合理的方法来重做你的逻辑,让编译器知道?如果是,那就去做;您的代码将更不容易出错。

有时没有合理的方法来处理错误,简单地忽略错误——从而处理错误的数据——会比崩溃更糟糕。那些是使用强制展开的时间。

我定期在我的整个代码库中搜索!,并审查它的每一次使用。很少有使用方法经得起推敲。(在撰写本文时,整个Siesta框架恰好是两个 实例。)

这并不是说你应该在你的代码中使用从来没有,而是说你应该使用用心地,不要让它成为默认选项。

总之(!): 当你声明了一个变量并且你确定这个变量有一个值

let assumedString: String! = "Some message..."
let implicitString: String = assumedString

否则你必须在每次传递值之后都这样做…

let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // requires an exclamation mark

如果你来自c语言家族,你会想“指向X类型对象的指针,它可能是内存地址0 (NULL)”,如果你来自动态类型语言,你会想“对象可能是X类型,但可能是未定义的类型”。这两种说法实际上都不正确,尽管迂回地说,第一个说法很接近。

你应该把它想象成一个物体:

struct Optional<T> {
var isNil:Boolean
var realObject:T
}

当你用foo == nil测试你的可选值时,它实际上返回foo.isNil,当你说foo!时,它返回foo.realObject,并断言foo.isNil == false。注意这一点很重要,因为当你执行foo!时,如果foo实际上是nil,这是一个运行时错误,所以通常你会想要使用条件let来代替,除非你非常确定该值不会为nil。这种技巧意味着语言可以是强类型的,而不必强制您测试是否所有地方的值都为nil。

实际上,它并不是这样做的,因为工作是由编译器完成的。在较高的级别上,有一个类型Foo?Foo是分开的,这可以防止接受类型Foo的函数接收到nil值,但在较低的级别上,可选值不是一个真正的对象,因为它没有属性或方法;实际上,它很可能是一个指针,在强制展开时,通过适当的测试,它可能是NULL(0)。

还有一种情况,你会看到感叹号是在一种类型上,比如:

func foo(bar: String!) {
print(bar)
}

这大致相当于接受一个强制展开的可选选项,即:

func foo(bar: String?) {
print(bar!)
}

你可以使用它来创建一个方法,该方法在技术上接受一个可选值,但如果它为nil,则会出现运行时错误。在当前版本的Swift中,这显然绕过了is-not-nil断言,所以你会有一个低级错误。通常不是个好主意,但在从另一种语言转换代码时可能很有用。

IN SIMPLE WORDS

# EYZ0

问问你自己

  • 类型person?是否有apartment成员/属性?或
  • 类型person是否有apartment成员/属性?

如果你不能回答这个问题,那么继续阅读:

要理解这一点,你可能需要对Generics有超基础的理解。参见here。Swift中的很多东西都是用泛型编写的。可选的包括

下面的代码已经从这个斯坦福的视频提供。强烈推荐你看前5分钟

“Optional”为enum,只有2个大小写

enum Optional<T>{
case None
case Some(T)
}


let x: String? = nil //actually means:


let x = Optional<String>.None

let x :String? = "hello" //actually means:


let x = Optional<String>.Some("hello")

var y = x! // actually means:


switch x {
case .Some(let value): y = value
case .None: // Raise an exception
}

可选的绑定:

let x:String? = something
if let y = x {
// do something with y
}
//Actually means:


switch x{
case .Some(let y): print)(y) // or whatever else you like using
case .None: break
}

当你说var john: Person?时,你实际上是指这样:

enum Optional<Person>{
case .None
case .Some(Person)
}

上面的枚举是否有任何名为apartment财产 ?你看到它了吗?这是 !然而,如果你打开它即做person!然后你可以…它在引擎盖下面做的是:Optional<Person>.Some(Person(name: "John Appleseed"))


如果你定义了var john: Person而不是:var john: Person?,那么你将不再需要使用!,因为Person本身有一个成员apartment


关于为什么有时不建议使用!来展开的讨论,请参阅这Q&

Simple the Optional variable allows nil to be stored.


var str : String? = nil


str = "Data"


To convert Optional to the Specific DataType, We unwrap the variable using the keyword "!"


func get(message : String){
return
}


get(message : str!)  // Unwapped to pass as String
整个故事开始于swift的一个叫做optional vars的特性。 这些变量可能有值,也可能没有值。一般来说,swift不允许我们使用没有初始化的变量,因为这可能会导致崩溃或意想不到的原因,也会为后门提供占位符。 因此,为了声明一个初始值不是确定的变量,我们使用'?'。 当声明这样一个变量时,要将其作为某个表达式的一部分使用,必须在使用之前将其展开,展开是一种发现变量值的操作,该操作适用于对象。如果你试图使用它们,没有展开,你会有编译时错误。 要打开一个可选的var变量,使用感叹号“!”。< / p >

有时候你知道这样的可选变量会被系统或者你自己的程序赋值,但是稍后,比如UI outlet,在这种情况下,我们不会用问号"?"来声明可选变量,而是用"!"

因此,系统知道这个用“!”声明的变量现在是可选的,没有值,但将在其生命周期的后期接收一个值。

因此,感叹号有两种不同的用法, 1. 声明一个变量,该变量将是可选的,并且以后一定会接收值 2. 在表达式中使用可选变量之前,将其展开

以上描述避免了太多的技术问题,我希望。

如果您将它用作可选选项,它将打开可选选项并查看其中是否有内容。如果你在If -else语句中使用它,它是NOT的代码。例如,

if (myNumber != 3){
// if myNumber is NOT 3 do whatever is inside these brackets.
)

可选变量可以包含值,也可以不包含值

案例1:var myVar:String? = "Something"

案例2:var myVar:String? = nil

现在如果你问myVar!,你告诉编译器返回一个值在情况1,它将返回"Something"

在情况2中,它会崩溃。

的意思!Mark将强制编译器返回一个值,即使它不存在。这就是为什么这个名字是力展开

员工:

john!.department

...告诉编译器:

  • 我知道john是可选的
  • 把它当成有价值的东西来使用
  • 如果没有,就崩溃

在生产中,使用guard letif let来处理无值和无效硬崩溃的情况。