我是Swift和iOS编码的新手,并且一直在编写我的第一个应用程序。虽然我的编程背景非常重要,但我来自Python和C#背景,其中几乎任何东西都可以None
或null
,并且由用户在运行时检查null 。我发现了这个 可空与非可空类型的整个概念"或"可选类型"在斯威夫特中令人困惑。
我理解核心概念是声明为类似myObject
的类型的变量不能设置为nil
。但是,如果我将其定义为myObject?
类型,则可以将值设置为nil
。
问题在于,当我查看我的代码设计时,感觉一切必须是"可空的"在我的代码中。感觉就像这意味着我没有正确思考我的代码应该如何运行,或者我错过了一些重要的理解。
让我们把最困惑的事情放在最简单的例子上。假设我有两个类 - 一个用于存储和管理某种数据,另一个用于提供对该数据的访问。 (例如,这可能类似于数据库连接,文件句柄或类似的东西。)让我们调用包含数据myData
的类和使用该数据的类{{1} }。
myObject
需要对myObject
的类级引用,因为它的许多方法都依赖于对该类的本地引用。因此,构造函数所做的第一件事就是生成数据连接,然后将其存储在局部变量myData
中。该变量需要在类级别定义,以便其他方法可以访问它,但它将在构造函数中分配。无法获得连接将导致某种异常,从而干扰类的创建。
我知道Swift有两种定义变量的方法:dataConnection
和var
,其中let
类似于某些语言' let
指令。由于数据连接将在整个课程期间持续存在,const
似乎是一个明显的选择。但是,我不知道如何通过let
定义一个类级变量,它将在运行时分配。因此,我使用像
let
在课外任何功能。
但是现在我必须处理可以为空的数据类型,并且每次在任何地方使用它时都要明确展开 。至少可以说令人困惑的是令人沮丧。
var dataConnection: myData?
这似乎是解决问题的极其冗长的方式。如果一个对象是func dealWithData() {
self.dataConnection.someFunctionToGetData() <- results in an unwrapping error.
self.dataConnection!.someFunctionToGetData() <- works.
let someOtherObjectUsingData: otherObject = self.getOtherObject() <- may result in error unless type includes ?
someOtherObjectUsingData.someMethod(self.dataConnection) <- unwrap error if type included ?
var myData = self.dataConnection!
someOtherObjectUsingData.someMethod(myData) <- works
}
func somethingNeedingDataObject(dataObject: myData?) {
// now have to explicitly unwrap
let myDataUnwrapped = myData!
...
}
,那么显式解包并不会导致运行时错误(可能被捕获和处理)?当把事物串在一起时,这往往是一场噩梦。我必须做类似的事情:
nil
我以前这样做的方法是你只需要定义一个变量,如果它被设置为null并且你尝试用它做一些事情,就会抛出一个异常(你可以捕获和处理) 。这简直不是Swift中的工作方式吗?这让我很困惑,几乎每次我尝试编译应用程序时,都会遇到很多错误(我只是让Xcode修复它们)。但这不是处理它的最佳方式。
我是否必须始终如一地处理包装和解包变量 - 即使是那些预期永远不会为空但却无法在编译时分配的变量?
答案 0 :(得分:1)
但是,我不知道如何通过let定义一个类级变量,它将在运行时分配。
这部分很容易。只需使用let
代替var
即可。使用Swift 1.2及更高版本,您可以延迟let
的实际分配。编译器足够聪明,可以进行流分析,并确保在所有路径中分配一次,并且只分配一次。因此,对于类范围的let,赋值也可以在构造函数中发生。
但是现在我必须处理可以为空的数据类型,并且每次在任何地方使用它时都要进行显式解包。
但这是隐含的未包装的Optionals所针对的。例如,StoryBoard将所有@IBOutlets
定义为隐式展开,因为语义非常明确:在进入viewDidLoad()
之后和之后的任何地方,展开都是安全的。如果你能为自己证明清晰的语义,你也可以这样做。
所以你有大约4个选择:
A)在类级别声明为隐式展开:
let dataConnection: MyData!
并被迫在构造函数中初始化它:
init() {
let whateverObj = someInitialCalculation()
dataConnection = whateverObj.someWayOfGettingTheConnection()
}
从那以后,你不需要&#39;!&#39 ;;应该清楚的是隐式展开始终是安全的。
B)如果初始化是可靠且明智的,那么在其声明中对其进行初始化,允许您放弃Optionals的整个概念:
let dataConnection = SomeClass.someStaticMethod()
C)在类级别声明为var
,作为隐式可选:
var dataConnection: MyData!
你不必在构造函数中初始化它;让它为nil
,直到可以/应该计算它的值。您仍然需要一些流量分析来证明某一点之后,如@IBOutlets的情况,访问它将始终有效
D)最不可预测的&#39;案件。将其声明为显式可选项,因为在整个类的生命周期中,数据连接将会出现:
var dataConnection: MyData?
func someMethodThatHandlesData() {
if let dC = dataConnection {
dc.handleSomeData()
}
else {
alert("Sorry, no data connection at the moment. Try again later.")
}
}
我认为你想象Swift总是迫使你走下D)。
就你的spaghetti-string代码而言,你想要查看Optional Chaining,只需要检查nil的最终结果。