Swift 可选链
查询、调用可能为’nil’的可选值上的属性、下标和方法的过程被定义为可选链。可选链返回两个值:
- 如果可选值包含一个’value’,则调用其相关的属性、方法和下标返回值
-
如果可选值包含’nil’,则其相关的属性、方法和下标返回nil
由于多个查询到方法、属性和下标被组合在一起,对一个链的失败将影响整个链,并且导致’nil’值的结果。
可选链作为强制解包的替代方式
可选链在可选值后指定’?’,在可选值返回一些值时调用属性、方法或下标。
- Optional Chaining ‘?’ :访问方法、属性和下标的可选链接’!’
-
可选值后面加上’?’来调用属性、方法或下标 :可选值后面加上’!’来强制解包属性、方法或下标的值
-
如果可选项为’nil’,则会优雅地失败 :如果可选项为’nil’,则强制解包会触发运行时错误
可选链接与’!’的程序
class ElectionPoll {
var candidate: Pollbooth?
}
lass Pollbooth {
var name = "MP"
}
let cand = ElectionPoll()
let candname = cand.candidate!.name
当我们使用playground运行上面的程序时,会得到以下结果 –
fatal error: unexpectedly found nil while unwrapping an Optional value
0 Swift 4 0x0000000103410b68
llvm::sys::PrintStackTrace(__sFILE*) + 40
1 Swift 4 0x0000000103411054 SignalHandler(int) + 452
2 libsystem_platform.dylib 0x00007fff9176af1a _sigtramp + 26
3 libsystem_platform.dylib 0x000000000000000b _sigtramp + 1854492939
4 libsystem_platform.dylib 0x00000001074a0214 _sigtramp + 1976783636
5 Swift 4 0x0000000102a85c39
llvm::JIT::runFunction(llvm::Function*, std::__1::vector > const&) + 329
6 Swift 4 0x0000000102d320b3
llvm::ExecutionEngine::runFunctionAsMain(llvm::Function*,
std::__1::vector<std::__1::basic_string, std::__1::allocator >,
std::__1::allocator<std::__1::basic_string, std::__1::allocator > > > const&,
char const* const*) + 1523
7 Swift 4 0x000000010296e6ba Swift 4::RunImmediately(Swift
4::CompilerInstance&, std::__1::vector<std::__1::basic_string,
std::__1::allocator >, std::__1::allocator<std::__1::basic_string,
std::__1::allocator > > > const&, Swift 4::IRGenOptions&, Swift 4::SILOptions
const&) + 1066
8 Swift 4 0x000000010275764b frontend_main(llvm::ArrayRef,
char const*, void*) + 5275
9 Swift 4 0x0000000102754a6d main + 1677
10 libdyld.dylib 0x00007fff8bb9e5c9 start + 1
11 libdyld.dylib 0x000000000000000c start + 1950751300
Stack dump:
0. Program arguments:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/
usr/bin/Swift 4 -frontend -interpret - -target x86_64-apple-darwin14.0.0 -
target-cpu core2 -sdk
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/
SDKs/MacOSX10.10.sdk -module-name main
/bin/sh: line 47: 15672 Done cat <<'SWIFT 4'
import Foundation
</std::__1::basic_string</std::__1::basic_string</std::__1::basic_string</std::
__1::basic_string
以上程序将 “选举调查” 声明为类名,并包含 “候选人” 作为成员函数。子类被声明为 “投票站”,并将 “名称” 声明为其成员函数,其初始值为 “MP”。通过创建一个可选实例 “cand” 来初始化对父类的调用,可选实例通过可选绑定操作符 “!” 初始化。由于基类中的值未声明,因此存储了 “nil” 值,从而通过强制解包过程返回了致命错误。
使用 “?” 的可选链编程示例
class ElectionPoll {
var candidate: Pollbooth?
}
class Pollbooth {
var name = "MP"
}
let cand = ElectionPoll()
if let candname = cand.candidate?.name {
print("Candidate name is \(candname)")
} else {
print("Candidate name cannot be retreived")
}
当我们在playground上运行上面的程序时,我们得到以下结果 –
Candidate name cannot be retreived
上面的程序将“选举投票”声明为类名,并包含“候选人”作为成员函数。子类声明为“投票亭”,其成员函数为“名称”,并初始化为“MP”。通过创建可选的“?”实例“cand”来初始化对超类的调用。由于值未在其基类中声明,“nil”值将被存储并由else处理程序块在控制台中打印。
为可选链接和访问属性定义模型类
Swift 4语言还提供了可选链接的概念,以声明多个子类作为模型类。这个概念对于定义复杂的模型和访问属性、方法和子属性的方法非常有用。
class rectangle {
var print: circle?
}
class circle {
var area = [radius]()
var cprint: Int {
return area.count
}
subscript(i: Int) -> radius {
get {
return area[i]
}
set {
area[i] = newValue
}
}
func circleprint() {
print("The number of rooms is \(cprint)")
}
var rectarea: circumference?
}
class radius {
let radiusname: String
init(radiusname: String) { self.radiusname = radiusname }
}
class circumference {
var circumName: String?
var circumNumber: String?
var street: String?
func buildingIdentifier() -> String? {
if circumName != nil {
return circumName
} else if circumNumber != nil {
return circumNumber
} else {
return nil
}
}
}
let rectname = rectangle()
if let rectarea = rectname.print?.cprint {
print("Area of rectangle is \(rectarea)")
} else {
print("Rectangle Area is not specified")
}
当我们使用游乐场运行上述程序时,我们会得到以下结果 –
Rectangle Area is not specified
通过可选链调用方法
class rectangle {
var print: circle?
}
class circle {
var area = [radius]()
var cprint: Int {
return area.count
}
subscript(i: Int) -> radius {
get {
return area[i]
}
set {
area[i] = newValue
}
}
func circleprint() {
print("Area of Circle is: \(cprint)")
}
var rectarea: circumference?
}
class radius {
let radiusname: String
init(radiusname: String) { self.radiusname = radiusname }
}
class circumference {
var circumName: String?
var circumNumber: String?
var circumarea: String?
func buildingIdentifier() -> String? {
if circumName != nil {
return circumName
} else if circumNumber != nil {
return circumNumber
} else {
return nil
}
}
}
let circname = rectangle()
if circname.print?.circleprint() != nil {
print("Area of circle is specified)")
} else {
print("Area of circle is not specified")
}
当我们在playground上运行上述程序时,我们得到以下结果 –
Area of circle is not specified
在circle()子类中声明的circleprint()函数通过创建名为’circname’的实例来调用。如果函数包含某个值,它将返回一个值,否则将通过检查语句’if circname.print?.circleprint() != nil’返回一条用户定义的打印消息。
通过可选链接访问下标
可选链接用于设置和检索下标值,以验证调用该下标是否返回一个值。在下标括号之前添加’?’以访问特定下标的可选值。
程序1
class rectangle {
var print: circle?
}
class circle {
var area = [radius]()
var cprint: Int {
return area.count
}
subscript(i: Int) -> radius {
get {
return area[i]
}
set {
area[i] = newValue
}
}
func circleprint() {
print("The number of rooms is \(cprint)")
}
var rectarea: circumference?
}
class radius {
let radiusname: String
init(radiusname: String) { self.radiusname = radiusname }
}
class circumference {
var circumName: String?
var circumNumber: String?
var circumarea: String?
func buildingIdentifier() -> String? {
if circumName != nil {
return circumName
} else if circumNumber != nil {
return circumNumber
} else {
return nil
}
}
}
let circname = rectangle()
if let radiusName = circname.print?[0].radiusname {
print("The first room name is \(radiusName).")
} else {
print("Radius is not specified.")
}
当我们使用playground运行上面的程序时,我们得到以下结果−
Radius is not specified.
在上面的程序中,未指定成员函数“radiusName”的实例值。因此,程序调用函数将只返回else部分,而要返回值,我们必须为特定的成员函数定义值。
class rectangle {
var print: circle?
}
class circle {
var area = [radius]()
var cprint: Int {
return area.count
}
subscript(i: Int) -> radius {
get {
return area[i]
}
set {
area[i] = newValue
}
}
func circleprint() {
print("The number of rooms is \(cprint)")
}
var rectarea: circumference?
}
class radius {
let radiusname: String
init(radiusname: String) { self.radiusname = radiusname }
}
class circumference {
var circumName: String?
var circumNumber: String?
var circumarea: String?
func buildingIdentifier() -> String? {
if circumName != nil {
return circumName
} else if circumNumber != nil {
return circumNumber
} else {
return nil
}
}
}
let circname = rectangle()
circname.print?[0] = radius(radiusname: "Diameter")
let printing = circle()
printing.area.append(radius(radiusname: "Units"))
printing.area.append(radius(radiusname: "Meter"))
circname.print = printing
if let radiusName = circname.print?[0].radiusname {
print("Radius is measured in \(radiusName).")
} else {
print("Radius is not specified.")
}
当我们使用playground运行上述程序时,得到以下结果 –
Radius is measured in Units.
在上面的程序中,指定了成员函数 ‘radiusName’ 的实例值。因此,对该函数的程序调用现在将返回值。
访问可选类型的下标
class rectangle {
var print: circle?
}
class circle {
var area = [radius]()
var cprint: Int {
return area.count
}
subscript(i: Int) -> radius {
get {
return area[i]
}
set {
area[i] = newValue
}
}
func circleprint() {
print("The number of rooms is \(cprint)")
}
var rectarea: circumference?
}
class radius {
let radiusname: String
init(radiusname: String) { self.radiusname = radiusname }
}
class circumference {
var circumName: String?
var circumNumber: String?
var circumarea: String?
func buildingIdentifier() -> String? {
if circumName != nil {
return circumName
} else if circumNumber != nil {
return circumNumber
} else {
return nil
}
}
}
let circname = rectangle()
circname.print?[0] = radius(radiusname: "Diameter")
let printing = circle()
printing.area.append(radius(radiusname: "Units"))
printing.area.append(radius(radiusname: "Meter"))
circname.print = printing
var area = ["Radius": [35, 45, 78, 101], "Circle": [90, 45, 56]]
area["Radius"]?[1] = 78
area["Circle"]?[1]--
print(area["Radius"]?[0])
print(area["Radius"]?[1])
print(area["Radius"]?[2])
print(area["Radius"]?[3])
print(area["Circle"]?[0])
print(area["Circle"]?[1])
print(area["Circle"]?[2])
当我们使用playground运行上面的程序时,我们得到以下结果-
Optional(35)
Optional(78)
Optional(78)
Optional(101)
Optional(90)
Optional(44)
Optional(56)
可通过引用它们的下标值来访问下标的可选值。可以使用subscript[0],subscript[1]等来访问。‘半径’的默认下标值首先被赋值为[35, 45, 78, 101],而‘圆’的默认下标值为[90, 45, 56]。然后将下标值更改为radius[0] = 78和Circle[1] = 45。
链接多级链接
多个子类也可以通过可选链接与其父类的方法、属性和下标进行链接。
可以链接多个可选链接 −
如果检索类型不是可选类型,可选链接将返回可选值。例如,通过可选链接获得String将返回String?的值。
class rectangle {
var print: circle?
}
class circle {
var area = [radius]()
var cprint: Int {
return area.count
}
subscript(i: Int) -> radius {
get {
return area[i]
}
set {
area[i] = newValue
}
}
func circleprint() {
print("The number of rooms is \(cprint)")
}
var rectarea: circumference?
}
class radius {
let radiusname: String
init(radiusname: String) { self.radiusname = radiusname }
}
class circumference {
var circumName: String?
var circumNumber: String?
var circumarea: String?
func buildingIdentifier() -> String? {
if circumName != nil {
return circumName
} else if circumNumber != nil {
return circumNumber
} else {
return nil
}
}
}
let circname = rectangle()
if let radiusName = circname.print?[0].radiusname {
print("The first room name is \(radiusName).")
} else {
print("Radius is not specified.")
}
当我们在playground中运行上述程序时,我们会得到以下结果-
Radius is not specified.
在上面的程序中,没有指定成员函数“radiusName”的实例值。因此,程序调用该函数将仅返回else部分,而要返回值,我们必须定义特定成员函数的值。
如果检索类型已经是可选的,则可选链接还将返回一个可选值。例如,如果通过可选链接访问String?,它将返回String?的值。
class rectangle {
var print: circle?
}
class circle {
var area = [radius]()
var cprint: Int {
return area.count
}
subscript(i: Int) -> radius {
get {
return area[i]
}
set {
area[i] = newValue
}
}
func circleprint() {
print("The number of rooms is \(cprint)")
}
var rectarea: circumference?
}
class radius {
let radiusname: String
init(radiusname: String) { self.radiusname = radiusname }
}
class circumference {
var circumName: String?
var circumNumber: String?
var circumarea: String?
func buildingIdentifier() -> String? {
if circumName != nil {
return circumName
} else if circumNumber != nil {
return circumNumber
} else {
return nil
}
}
}
let circname = rectangle()
circname.print?[0] = radius(radiusname: "Diameter")
let printing = circle()
printing.area.append(radius(radiusname: "Units"))
printing.area.append(radius(radiusname: "Meter"))
circname.print = printing
if let radiusName = circname.print?[0].radiusname {
print("Radius is measured in \(radiusName).")
} else {
print("Radius is not specified.")
}
运行以上程序时,我们使用playground,我们得到以下结果−
Radius is measured in Units.
在上面的程序中,指定了“radiusName”的成员函数的实例值。因此,对函数的程序调用将返回值。
使用可选返回值链接方法
可选链接可用于访问定义的子类方法。
class rectangle {
var print: circle?
}
class circle {
var area = [radius]()
var cprint: Int {
return area.count
}
subscript(i: Int) -> radius {
get {
return area[i]
}
set {
area[i] = newValue
}
}
func circleprint() {
print("Area of Circle is: \(cprint)")
}
var rectarea: circumference?
}
class radius {
let radiusname: String
init(radiusname: String) { self.radiusname = radiusname }
}
class circumference {
var circumName: String?
var circumNumber: String?
var circumarea: String?
func buildingIdentifier() -> String? {
if circumName != nil {
return circumName
} else if circumNumber != nil {
return circumNumber
} else {
return nil
}
}
}
let circname = rectangle()
if circname.print?.circleprint() != nil {
print("Area of circle is specified)")
} else {
print("Area of circle is not specified")
}
当我们在playground上运行上述程序时,我们得到以下结果 –
Area of circle is not specified