Swift Functions
1. Optional Parameters#
Swift中可选参数的行为很容易误导人, 让人错误的以为可选参数在调用函数的时候可以不提供, 然而并不是这样, 可选参数只是允许该参数值为 nil
, 即使该值是 nil
, 也必须显示提供.
// withGreeting: external parameter name (or argument label)
func greet(name: String, withGreeting greeting: String?) {
if let greeting = greeting {
print("\(greeting), \(name)!")
} else {
print("\(name)!")
}
}
// 即使 withGreeting 为 nil, 也必须提供
greet(name: "Alice", withGreeting: nil) // No greeting: "Alice!"
greet(name: "Bob", withGreeting: "Hi") // Uses the provided greeting: "Hi, Bob!"
“if let greeting = greeting” 这不是这个赋值语句吗, 怎么能用来判断呢?
在 Swift 中,
if let
常用来 unwrap optional value,名字叫可选绑定(optional binding), 形式如下:if let unwrappedValue = optionalValue { // 使用 unwrappedValue } else { // optionalValue 为 nil }
2. Default Parameters#
默认参数为参数提供一个默认值,这样在调用函数时可以忽略这些参数。
func greet(name: String, withGreeting greeting: String = "Hello") {
print("\(greeting), \(name)!")
}
greet(name: "Alice") // 输出: "Hello, Alice!"
greet(name: "Bob", withGreeting: "Hi") // 输出: "Hi, Bob!"
func greet(name: String, withGreeting greeting: String) {
print("\(greeting), \(name)!")
}
// compile error: 'nil' is not compatible with expected argument type 'String'
greet(name: "Alice", withGreeting: nil)
分清你要的东西是什么, 是允许参数为 nil 还是调用函数的时候不提供参数.
3. Optional Chaining#
尝试访问可选值的属性或方法时会用到, 如果可选值有值, 那么调用成功, 如果可选值是 nil
, 则调用返回 nil
, 整个过程不会因为尝试访问 nil
的属性而导致程序崩溃.
class Cat {
var name: String
init(name: String) {
self.name = name
}
}
func printCatName(_ cat: Cat?) {
// 若 cat 为 nil, 不会出现 nil 不存在 name 属性的异常
if let name = cat?.name {
print("The cat's name is \(name).")
} else {
print("No cat provided.")
}
}
注意 Optional chaining 可以用于一切尝试访问可选值属性的行为, 以避免尝试访问 nil 的属性不存在异常发生, 上面的例子只是参数为可选值的情况,
观察下面这段代码, 从逻辑上来说 selectedTaskItem 在 if 语句后必不为空, 所以下意识会想到 这里的 Optional Chaining 是不是多余的?
@State var selectedTaskItem: TaskItem?
withAnimation {
if selectedTaskItem == nil {
selectedTaskItem = TaskItem(context: viewContext)
}
selectedTaskItem?.created = Date()
selectedTaskItem?.name = name
selectedTaskItem?.dueDate = dueDate
selectedTaskItem?.scheduleTime = scheduleTime
self.presentationMode.wrappedValue.dismiss()
}
答案并不是, 如果直接写为 selectedTaskItem.name = name
, 则编译器会报错: Value of optional type 'TaskItem?' must be unwrapped to refer to member 'created' of wrapped base type 'TaskItem'
这是因为在 Swift 中,即使你在条件分支中对一个可选变量进行了赋值,这个变量的类型仍然保持为可选类型 (Optional
)。因此,即使我们已经确信该可选变量不为 nil
,编译器仍要求我们对其进行安全访问,因为从类型系统的角度来看,该变量仍然是可选的。
4. Parameter Labels#
关于参数的 label, 如果不指定, 参数的 label 被设置为参数名, 即调用函数的时候必须指定label, 如
getName(id: 123)
, id 就是label, 必须指定不可以写为getName(123)
因此常有些
_
来忽略 label, 为了调用函数的时候不用显示指出 label.
5. Unwrapping Optional Value#
SafariView 是个自定义 view, 构造函数需要传递一个 URL 类型的参数, 而下面代码却报错,
// error: Value of optional type 'URL?' must be unwrapped to a value of type 'URL'
SafariView(url: URL(string: urlString))
根据报错信息可以看出, URL构造函数应该是返回了一个 optional value, 即可能为 nil, 因此编译器提示我们要 unwrap URL?
,
// URL 的一个构造函数
init?(string: String)
有多种方法 unwrap 可选类型, 分别为 Optional Binding, Force Unwrapping 和 Nil-Coalescing, 其中 Force Unwrapping 不推荐使用, 因为可能导致运行时错误.
// Optional Binding
if let url = URL(string: urlString) {
SafariView(url: url)
} else {
Text("Invalid URL")
}
// Force Unwrapping
let urlString = "https://www.apple.com"
let url = URL(string: urlString)! // Force unwrap
// Nil-Coalescing
let url = URL(string: urlString) ?? URL(string: "https://www.example.com")!
在这个地方的 Nil-Coalescing 也用到了 Force Unwrapping, 因为 URL() 总是会返回可选类型, 所以此时最优解是使用 Optional Binding.