💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、豆包、星火、月之暗面及文生图、文生视频 广告
# A Quick Start Of Swift <h2 id="0"></h2> * [ 1. 学习目标](#1) * [ 2. 基本类型](#2) * [ 3. 控制流](#3) * [ 4. 函数和方法](#4) * [ 5. 类和初始化器](#5) * [ 6. 枚举和结构体](#6) * [ 7. Swift 和 Cocoa Touch](#7) * [ 8. 附录](#8) <br> <h2 id="1">1. 学习目标</h2> 在本课程结束后,你将能够: + 定义常量和变量,及其区别; + 了解隐式类型定义的使用和何时使用显示类型定义。 + 了解可选项的优点以及可选项绑定 + 了解可选项以及隐式解包可选项的区别 + 了解条件语句以及循环的功能 + 了解函数、方法以及初始化器的区别 + 了解类、结构体以及枚举的区别 + UIKit的导入和使用 + 创建一个iOS App:魔法水晶球 <br> [返回目录](#0) <br> <h2 id="2">2. 基本类型</h2> **常量** 在为其赋值之后值不能再次改变。常量的值在编译时并不要求已知,但是你必须为其赋值一次。 ``` let name:String = "OceanHorn" print(name) let language:String language = "Swift" ``` > 一个扔掉钥匙的透明保险箱,你知道里面有什么却不能改变里面的东西。 > > 一个沙漏,看得见里面的沙子,却不能改变沙漏的计时长度。 > > 一件过去发生的事,还是接受结果,活在当下吧。 **变量** 如其名,在定义之后可以改变其值,但是不能改变其类型。 ``` var age:Int = 18 print(age) age = 19 age = 20 age = "一朵花" // 会报错 ``` 对于常量和变量,在声明的时候直接给它们赋值就可以让编译器推断它们的类型,就可以省略其类型(**类型推断**)。比如: ``` let lastName = "Guo" print(lastName) var weight = 160 weight += 3 // 每逢佳节胖三斤 print(weight) ``` 如果给出的信息不够还是需要指明其类型,比如: ``` let explicitDouble: Double = 70 ``` 常量与变量名不能包含数学符号,箭头,保留的(或者非法的)Unicode 码位,连线与制表符。也不能以数字开头,但是可以在常量与变量名的其他地方包含数字。 一旦你将常量或者变量声明为确定的类型,你就不能使用相同的名字再次进行声明,或者改变其存储的值的类型。同时,你也不能将常量与变量进行互转。 **类型转换** 值永远不会隐式地转换为其他类型。如果你需要将一个值转换为不同的类型,需要使用对应的类型显示地声明。 ``` let label = "The width is " let width = 94 let widthLabel = label + String(width) print(widthLabel) ``` **字符串插值** 还有一种更简单的方法来把值加入字符串:将值写在圆括号里,然后再在圆括号的前边写一个反斜杠 (`\`) ,举个例子: ``` let apples = 3 let oranges = 5 let appleSummary = "I have \(apples) apples." let fruitSummary = "I have \(apples + oranges) pieces of fruit." ``` **可选类型** 可以利用可选类型来处理值可能缺失的情况。可选类型意味着: + 这里有一个值,他等于x 或者 + 这里根本没有值 在定义一个值得时候在其类型后面加一个(`?`)就可以将其定义为一个可选类型。 ``` let optionalInt: Int? = 9 print(optionalInt) ``` 一旦你**确定可选类型中包含值**,你可以在可选的名字后面加一个感叹号 ( `!` ) 来获取值,感叹号的意思就是说“我知道这个可选类型里边有值,展开吧。”这就是所谓的可选类型的强制展开。 ``` let actualInt: Int = optionalInt! print(actualInt) // 注意比较与之前打印的区别 if optionalInt != nil { print("convertedNumber has an integer value of \(optionalInt!).") } ``` 可选类型在 Swift 中相当普遍,在值可能有也可能不存在的场景下非常有用。最典型的是在尝试强制类型转换的时候。 ``` var myString = "7" // 尝试把“7”改成“qi” var possibleInt = Int(myString) print(possibleInt) ``` **数组** 数组以有序的方式来储存相同类型的值。相同类型的值可以在数组的不同地方多次出现。定义数组使用(`[]`),访问数组成员可以用`[index]`,数组索引从0开始。 ``` var ratingList = ["Poor", "Fine", "Good", "Excellent"] ratingList[1] = "OK" ratingList ``` 创建一个空数组可以使用初始化器: ``` let emptyArray = [String]() ``` **隐式解析可选类型** 有时在一些程序结构中可选类型一旦被设定值之后,就会一直拥有值。在这种情况下,就可以去掉检查的需求,也不必每次访问的时候都进行展开,因为它可以安全的确认每次访问的时候都有一个值。 隐式解析可选类型在故事版场景中很普遍,但是同样可以像非可选类型那样来使用,每次访问的时候都不需要展开。下面的例子展示了在访问被明确为 `String` 的可选类型展开值时,可选字符串和隐式解析可选类型字符串的行为区别: ``` let possibleString: String? = "An optional string." let forcedString: String = possibleString! // requires an exclamation mark let assumedString: String! = "An implicitly unwrapped optional string." let implicitString: String = assumedString // no need for an exclamation mark if assumedString != nil { print(assumedString) } if let definiteString = assumedString { print(definiteString) } ``` 在实际使用中很少需要自己创建隐式解析可选类型。通常情况下在代码与故事版之间的接口中出现。 <br> [返回目录](#0) <br> <h2 id="3">3. 控制流</h2> Swift 有两种控制流语句。 + 条件语句,像`if`, `switch`,判断条件是否满足,如果满足(值为`true`)则执行满足条件时的代码 + 循环语句,像`for-in`,`while`,来将一段代码多次执行。 `if`可以与`else`语句配合,,用来在 `if` 条件为 `false` 的时候使用, 多个`if`语句可以进行多层嵌套。 ``` let number = 23 if number < 10 { print("The number is small") } else if number > 100 { print("The number is pretty big") } else { print("The number is between 10 and 100") } ``` `for-in`语句与`if`语句也可以嵌套来实现更复杂的逻辑: ``` let individualScores = [75, 43, 103, 87, 12] var teamScore = 0 for score in individualScores { if score > 50 { teamScore += 3 } else { teamScore += 1 } } print(teamScore) ``` 在`if`语句中使用**可选绑定**来检查一个可选类型是否存在一个值: ``` var optionalName: String? = "John Smith" // 改为nil试试 var greeting = "Hello!" if let name = optionalName { greeting = "Hello, \(name)" } ``` `where`语句也可以被添加到一个判断case中来实现进一步的判断,当所有条件都满足的时候才会执行相关的代码: ``` var optionalHello: String? = "Hello" if let hello = optionalHello where hello.hasPrefix("H"), let name = optionalName { greeting = "\(hello), \(name)" } ``` 可以用`Range`来创建一个索引范围以便在循环中获得索引,通过使用范围运算符`..<`来创建索引范围。 ``` var firstForLoop = 0 for i in 0..<4 { firstForLoop += i } print(firstForLoop) ``` `..<`创建的范围不包括最后一个,如`0..<3`包含0,1,2 但不包含3。`...`创建的范围包含最后一个索引。`0...3`包含0,1,2,3。 如果不关心索引,可以使用通配符`_`来替换索引: ``` var secondForLoop = 0 for _ in 0...4 { secondForLoop += 1 } print(secondForLoop) ``` <br> [返回目录](#0) <br> <h2 id="4">4. 函数和方法</h2> 函数是一个独立的代码块,用来执行特定的任务。通过给函数一个名字来定义它的功能,并且在需要的时候,通过这个名字来“调用”函数执行它的任务。 使用`func`来定义一个函数。函数可以有0个到多个参数,用`name: Type`来表示。在调用函数时,需要为该函数的参数传递相应的值。函数可以没有返回值,也可以有一个返回值,用`-> Type`表示。函数内部的具体实现在其后的`{}`中。 ``` func greet(name: String, day: String) -> String { return "Hello \(name), today is \(day)." } ``` 调用函数通过在函数名后的圆括号中相应的参数位值传递符合函数要求的值即可。通常情况下,对于第一个参数参数名是省略的,后面的参数前面会有对应的参数名。 ``` greet("Anna", day: "Tuesday") greet("Bob", day: "Friday") greet("Charlie", day: "a nice day") ``` 特定类型中定义的函数成为**方法**。方法被明确的关联到它所定义的类型中,仅能够被该类 或该类的实例调用。 ``` let exampleString = "hello" if exampleString.hasSuffix("lo") { print("ends in lo") } ``` 像上面的那样,可以使用点语法来调用方法。在调用时第一个参数忽略掉其参数名,后面的参数名都要加上。比如,下面的`Array`的方法有两个参数,调用时仅需要 给出第二个参数名。(实际编写代码时都是自动提示的,仅需要传递参数值即可) ``` var array = ["apple", "banana", "dragonfruit"] array.insert("cherry", atIndex: 2) array ``` <br> [返回目录](#0) <br> <h2 id="5">5. 类和初始化器</h2> 面向对象编程中,编程的大量工作聚焦在对象间的交互上。对象是一个类的实例, 而类可以看成是对象的蓝图或者抽象。类中以属性和方法的方式存储了关于一系列对象共有的属性及使用方法(行为)。 通过在`class`后面添加一个名称来创建一个类。类中属性的定义方法跟定义常量与变量的方法相同,类中方法的定义跟定义函数的方法也相同。 ``` class Shape { var numberOfSides = 0 func simpleDescription() -> String { return "A shape with \(numberOfSides) sides." } } ``` 对于这个简单的类,创建一个实例并调用其方法如下: ``` var shape = Shape() shape.numberOfSides = 3 var shapeDescription = shape.simpleDescription() ``` `Shape`还少了一个重要的组成部分:一个初始化器。 **初始化器**用来为类、结构体或者枚举完成准备实例的过程。涉及到为每个属性设置一个初始化值,执行其他一些初始化操作等。使用`init`来创建初始化器。 ``` class NamedShape { var numberOfSides = 0 var name: String init(name: String) { self.name = name } func simpleDescription() -> String { return "A shape with \(numberOfSides) sides." } } ``` 注意区分在初始化器中,是怎样区分`name`属性与`name`参数的。 需要特别提出的是:类中的每一个属性都应该被赋值,要么通过定义时赋值,要么通过初始化器赋值。 对于`NamedShape`通过初始化器创建实例如下,在调用初始化器时需要包含所有的参数及其对应的变量名。 ``` let namedShape = NamedShape(name: "my named shape") ``` <br> [返回目录](#0) <br> <h2 id="6">6. 枚举和结构体</h2> 在 Swift 中,类不是创建数据类型的唯一途径。 枚举和结构体拥有跟类相似的能力, 而且在某些场景下更有效。 **枚举** 枚举为一组相关值定义了一个普通的类型,使得可以在编程时使用这些类型变得更加安全。枚举也可以拥有与其值相关的方法。 使用`enum`来创建枚举。 ``` enum Rank: Int { case Ace = 1 case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten case Jack, Queen, King func simpleDescription() -> String { switch self { case .Ace: return "ace" case .Jack: return "jack" case .Queen: return "queen" case .King: return "king" default: return String(self.rawValue) } } } let ace = Rank.Ace let aceRawValue = ace.rawValue ``` 在上面的例子中,枚举的原始值类型是`Int`,所以你必须指定第一个枚举项的原始值,其他项的原始值会自动顺序加1。也可以使用字符串或者浮点数作为枚举项的原始值。使用`rawValue`属性来访问枚举项的原始值。 使用` init?(rawValue:)`初始化器来通过原始值创建对应枚举类型的一个实例。 ``` if let convertedRank = Rank(rawValue: 3) { let threeDescription = convertedRank.simpleDescription() } ``` 枚举成员的值都是自然值,不是他们原始值的另外一种表述方法。实际上,对于一个没有意义的原始值,你是不需要提供的。 ``` enum Suit { case Spades, Hearts, Diamonds, Clubs func simpleDescription() -> String { switch self { case .Spades: return "spades" case .Hearts: return "hearts" case .Diamonds: return "diamonds" case .Clubs: return "clubs" } } } let hearts = Suit.Hearts let heartsDescription = hearts.simpleDescription() ``` 注意上面代码中枚举成员的两种使用方法: + 定义常量`heart`时,类型尚不确定,后面需要使用`Suit.Hearts`为其赋值. + Suit内部,`switch self`(`switch`本课未讲)时已经明确`self`为`Suit`类型,故可以直接使用缩写的形式。 实际后面这样也是可以的: ``` var someCase = Suit.Hearts print(someCase.simpleDescription()) someCase = .Diamonds let someDescription = someCase.simpleDescription() ``` **结构体** 使用`struct`来创建结构体。 ``` struct Card { var rank: Rank var suit: Suit func simpleDescription() -> String { return "The \(rank.simpleDescription()) of \(suit.simpleDescription())" } } let threeOfSpades = Card(rank: .Three, suit: .Spades) let threeOfSpadesDescription = threeOfSpades.simpleDescription() ``` <br> [返回目录](#0) <br> <h2 id="7">7. Swift 和 Cocoa Touch</h2> Swift 在设计时就考虑了与 Cocoa Touch 的无缝连接,以便将 Swift 用于 iOS App 的开发。 到目前为止我们使用的数据类型都来源于 Swift 的标准库,比如 `String`,`Array` 等。 Swift 标准库是为 Swift 设计的并嵌入其中,它有一系列的数据类型。 当编写 iOS 应用时,你将使用除 Swift 标准库之外更多的库。其中一个在开发 App 时最常见的是 UIKit 。 UIKit 中包含了应用交互层相关的各种类。 导入 UIKit 的方式: ``` import UIKit ``` 导入之后就可以使用 Swift 语法和 UIKit 中的各种类,来实现各种效果。 ``` let redSquare = UIView(frame: CGRect(x: 0, y: 0, width: 44, height: 44)) redSquare.backgroundColor = UIColor.redColor() ``` <br> [返回目录](#0) <h2 id="8">8. 附录</h2> [课程项目链接](https://git.oschina.net/OceanHorn/MagicCrystalBall) [the-swift-programming-language-in-chinese ](https://github.com/numbbbbb/the-swift-programming-language-in-chinese) [返回目录](#0)