Swift 为什么要创造;隐式展开的选项“;,因为这意味着你知道那里';什么是价值?

Swift 为什么要创造;隐式展开的选项“;,因为这意味着你知道那里';什么是价值?,swift,design-patterns,optional,Swift,Design Patterns,Optional,为什么要创建“隐式展开可选”而不是只创建正则变量或常量? 如果您知道它可以成功地展开,那么为什么要首先创建一个可选的? 例如,这是为什么: let someString: String! = "this is the string" 这将比: let someString: String = "this is the string" 如果“optionals表示一个常量或变量允许有‘无值’”,但“有时从程序的结构中可以清楚地看出,一个可选的值在第一次设置该值之后总是有一个值”,那么将其设为可

为什么要创建“隐式展开可选”而不是只创建正则变量或常量? 如果您知道它可以成功地展开,那么为什么要首先创建一个可选的? 例如,这是为什么:

let someString: String! = "this is the string"
这将比:

let someString: String = "this is the string"
如果“optionals表示一个常量或变量允许有‘无值’”,但“有时从程序的结构中可以清楚地看出,一个可选的值在第一次设置该值之后总是有一个值”,那么将其设为可选的首要目的是什么?
如果您知道一个可选对象总是会有一个值,那么它不是可选的吗?

考虑一个对象的情况,该对象在构造和配置时可能具有nil属性,但之后是不可变和非nil的(NSImage通常以这种方式处理,尽管在这种情况下,有时变异仍然很有用)。隐式非包装optionals将对其代码进行大量清理,安全损失相对较低(只要有一个担保,它就安全)

(编辑)但要清楚:常规选项几乎总是可取的。

一行(或几行)简单的示例不能很好地涵盖选项的行为-是的,如果您声明一个变量并立即为其提供一个值,那么可选选项就没有意义了

到目前为止,我看到的最好的情况是在对象初始化之后进行设置,然后使用“保证”遵循该设置的方法,例如在视图控制器中:

class MyViewController: UIViewController {

    var screenSize: CGSize?

    override func viewDidLoad {
        super.viewDidLoad()
        screenSize = view.frame.size
    }

    @IBAction printSize(sender: UIButton) {
        println("Screen size: \(screenSize!)")
    }
}
我们知道加载视图后将调用
printSize
,这是一个连接到该视图内控件的操作方法,我们确保不会以其他方式调用它。因此,我们可以使用
节省一些可选的检查/绑定。斯威夫特无法识别这种保证(至少在苹果解决停止问题之前),所以你告诉编译器它是存在的


不过,这在某种程度上破坏了类型安全性。如果你的“保证”不总是有效的话,任何你有一个隐式解包选项的地方都是你的应用程序可能崩溃的地方,所以这是一个需要谨慎使用的功能。此外,使用
总是让人觉得你在大喊大叫,没有人喜欢这样。

隐式展开的选项对于将属性表示为非可选属性非常有用,因为它实际上需要在封面下是可选的。这对于在两个相关对象之间“打结”通常是必要的,因为每个对象都需要对另一个对象的引用。当两个引用实际上都不是可选的,但其中一个引用在对进行初始化时需要为nil时,这是有意义的

例如:

// These classes are buddies that never go anywhere without each other
class B {
    var name : String
    weak var myBuddyA : A!
    init(name : String) {
        self.name = name
    }
}

class A {
    var name : String
    var myBuddyB : B
    init(name : String) {
        self.name = name
        myBuddyB = B(name:"\(name)'s buddy B")
        myBuddyB.myBuddyA = self
    }
}

var a = A(name:"Big A")
println(a.myBuddyB.name)   // prints "Big A's buddy B"
任何
B
实例都应该始终有一个有效的
myBuddyA
引用,因此我们不想让用户将其视为可选的,但我们需要它是可选的,这样我们就可以在有
a
引用之前构造一个
B


然而!这种相互参考的要求通常是紧耦合和不良设计的表现。如果你发现自己依赖隐式展开的选项,你可能应该考虑重构来消除交叉依赖。

< P>隐式选择的原理更容易解释,首先看强制解包的原理。< /P> 使用!运算符,这意味着您确定代码没有bug,并且可选的已在展开代码的位置具有一个值。没有这个!运算符,您可能只需要使用可选绑定进行断言:

 if let value = optionalWhichTotallyHasAValue {
     println("\(value)")
 } else {
     assert(false)
 }
这可不像你想象的那么好

println("\(value!)")

现在,隐式optionals允许您表示在所有可能的流中,有一个可选项,您希望它在展开时总是有一个值。因此,它只是在帮助你上了一步——通过放松写作的要求!每次展开,并确保在您对流程的假设错误的情况下,运行时仍会出错。

隐式展开选项是一种务实的折衷方案,可使混合环境中的工作更加轻松,该环境必须与现有Cocoa框架及其约定互操作,同时也允许分步迁移到更安全的编程模式——没有空指针——由Swift编译器强制执行

说:


当一个可选项的值在第一次定义该可选项之后立即被确认存在时,隐式展开的可选项是有用的,并且可以肯定地假设该可选项在此后的每一点上都存在。Swift中隐式展开选项的主要用途是在类初始化期间,如中所述。
……
您可以将隐式展开的可选项视为授予在使用该可选项时自动展开该可选项的权限。不是每次使用可选项时都在其名称后面加一个感叹号,而是在声明可选项的类型时在其类型后面加一个感叹号

这归结为一些用例,其中属性的非
nil
-ness是通过使用约定建立的,并且在类初始化期间编译器不能强制执行。例如,从NIB或故事板初始化的
UIViewController
属性,其中初始化分为单独的阶段,但在
viewDidLoad()
之后,您可以假设属性通常存在。否则,为了满足编译器的要求,必须使用 , 或 只是为了模糊代码的主要目的

Swift手册的上述部分也指:

但是,还有第三种情况,在这种情况下,两个属性都应该始终有一个值,并且一旦初始化完成,两个属性都不应该是
nil
。在这种情况下,将一个类上的无主属性与另一个c类上的隐式未包装可选属性相结合是非常有用的
class MyView: UIView {
    @IBOutlet var button: UIButton!
    var buttonOriginalWidth: CGFloat!

    override func awakeFromNib() {
        self.buttonOriginalWidth = self.button.frame.size.width
    }
}
class FileSystemItem {
}

class Directory : FileSystemItem {
    lazy var contents : [FileSystemItem] = {
        var loadedContents = [FileSystemItem]()
        // load contents and append to loadedContents
        return loadedContents
    }()
}
class Country {
    let name: String
    var capitalCity: City! // Apple finally correct this line until 2.0 Prerelease (let -> var)
    init(name: String, capitalName: String) {
        self.name = name
        self.capitalCity = City(name: capitalName, country: self)
    }
}

class City {
    let name: String
    unowned let country: Country
    init(name: String, country: Country) {
        self.name = name
        self.country = country
    }
}
//Optional string with a value
let optionalString: String? = "This is an optional String"

//Declaration of an Implicitly Unwrapped Optional String
let implicitlyUnwrappedOptionalString: String!

//Declaration of a non Optional String
let nonOptionalString: String

//Here you can catch the value of an optional
implicitlyUnwrappedOptionalString = optionalString

//Here you can't catch the value of an optional and this will cause an error
nonOptionalString = optionalString
var implicitlyUnwrappedOptional: String! //<- Implicitly Unwrapped Optional
var nonNil: String = ""
var optional: String?

func foo() {
    //get a value
    nonNil.count
    optional?.count
    
    //Danderour - makes a force unwrapping which can throw a runtime error
    implicitlyUnwrappedOptional.count
    
    //assign to nil
//        nonNil = nil //Compile error - 'nil' cannot be assigned to type 'String'
    optional = nil
    implicitlyUnwrappedOptional = nil
}