- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
我是 Swift 和 iOS 编码的新手,一直在努力编写我的第一个应用程序。虽然我的编程背景非常重要,但我来自 Python 和 C# 背景,其中几乎所有内容都可以是 None
或 null
并且由用户在运行时检查一个空。我发现 Swift 中“可空类型与不可空类型”或“可选类型”的整个概念令人困惑。
我理解核心概念是不能将声明为myObject
类型的变量设置为nil
。但是,如果我将它定义为 myObject?
类型,那么该值可以设置为 nil
。
问题是,当我查看我的代码设计时,感觉代码中的一切 都必须是“可空的”。感觉这要么意味着我没有正确思考我的代码应该如何运行,要么我缺少一些关键的理解。
让我们举一个我感到困惑的最简单的例子。假设我有两个类——一个存储和管理某种数据,另一个提供对该数据的访问。 (这方面的一个例子可能是数据库连接、文件句柄或类似的东西。)让我们调用包含数据的类 myData
和使用该数据的类 myObject
.
myObject
将需要对 myData
的类级引用,因为它的许多方法都依赖于对类的本地引用。因此,构造函数做的第一件事就是生成数据连接,然后将其存储在局部变量dataConnection
中。该变量需要在类级别定义,以便其他方法可以访问它,但它将在构造函数中分配给它。未能获得连接将导致某种异常,这将干扰类的创建。
我知道 Swift 有两种定义变量的方法:var
和 let
,其中 let
类似于某些语言的 const
指令。由于数据连接将贯穿类的整个生命周期,let
似乎是一个显而易见的选择。但是,我不知道如何通过 let
定义将在运行时分配的类级变量。因此,我使用类似
var dataConnection: myData?
在任何函数之外的类中。
但现在我必须处理可为 null 的数据类型,并且每次在任何地方使用它时都要显式展开。至少可以说这是令人沮丧且相当困惑的。
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
,显式解包本身不会导致运行时错误(可以被捕获和处理)吗?当把事情串在一起时,这往往是一场噩梦。我不得不做类似的事情:
self.dataConnection!.somethingReturningAnObject!.thatObjectsVariable!.someMethod()
var myData? = self.dataConnection
var anotherObject? = myData!.somethingReturningAnObject
...
我习惯这样做的方式是您只需定义一个变量,如果它被设置为 null 并且您尝试用它做一些事情,就会抛出一个异常(您可以捕获和处理)。这难道不是 Swift 中的工作方式吗?这让我很困惑,几乎每次我尝试编译一个应用程序时,我都会遇到很多关于这个的错误(我只是让 Xcode 修复它们)。但这不是处理它的最佳方法。
我是否必须始终如一地处理包装和展开变量 - 即使是那些一开始就应该永远不会为 null 但根本无法在编译时分配的变量?
最佳答案
However, I do not know how to define a class-level variable via let which will be assigned at runtime.
这部分很简单。只需使用 let
而不是 var
。使用 Swift 1.2 及更高版本,您可以延迟 let
的实际赋值。编译器足够聪明,可以进行流分析并确保它在所有路径中只分配一次。因此,对于类范围的 let,赋值也可以发生在构造函数中。
But now I have to deal with the nullable data type, and do explicit unwrapping every time I use it anywhere.
但这就是隐式解包 Optionals 的用途。例如,StoryBoard 将所有 @IBOutlets
定义为隐式解包,因为语义非常清楚:在进入 viewDidLoad()
时以及之后的任何地方,解包都是安全的。如果您可以向自己证明清晰的语义,您也可以这样做。
所以你有大约 4 个选择:
A) 在类级别声明为隐式解包:
let dataConnection: MyData!
并强制在构造函数中初始化它:
init() {
let whateverObj = someInitialCalculation()
dataConnection = whateverObj.someWayOfGettingTheConnection()
}
从那时起你就不需要'!';应该清楚,隐式解包总是安全的。
B) 如果此时它的初始化是可靠和合理的,就在它的声明中初始化它,让你放弃整个 Optionals 的概念:
let dataConnection = SomeClass.someStaticMethod()
C) 在类级别声明为 var
,作为隐式可选:
var dataConnection: MyData!
您不必在构造函数中初始化它;让它成为 nil
直到它的值可以/应该被计算。你仍然需要一些流量分析来证明在某个点之后,就像@IBOutlets 的情况一样,访问它总是有效的
D) 最“不可预测”的情况。将其声明为显式可选,因为在类的整个生命周期中,数据连接会来来去去:
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 路)。
至于你的意大利面条代码,你想研究可选链,只需要检查最终结果是否为 nil。
关于Swift:对可空/可选类型感到困惑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31686040/
我正在尝试学习Rust。我正在阅读一本书online,该书实现了unix程序cat。现在,我试图读取作为像cargo run file1.txt file2.txt这样的参数传递的文件的内容,但是程序
我在 GHC 8.0.1 中遇到了一个带有种类索引 (?) GADT 的奇怪情况,其中在类型与种类签名中引入 foralls 会产生不同的类型检查行为。 考虑以下数据类型: {-# LANGUAGE
我正在使用 Perl 5.10 开发应用程序,HTML::Mason和 Apache 2.2。这是我第一次在大型项目中使用 Perl 5.10。我每隔一段时间就会出现奇怪的行为。应用程序因一个非常奇怪
我正在尝试将文件上传到aws中的rust中,因为我使用的是 rusoto_s3 的s3 rust客户端,当这些部分从单个线程发送时,我设法使分段上传代码正常工作不是我想要的,我想上传大文件,并且希望能
我是一名优秀的程序员,十分优秀!