原视频:www.bilibili.com/video/BV1uZ…
Swift入门-简洁版(1 / 3)
一、学前须知
二、Swift简介
- Swift是Apple在2014年6月WWDC发布的全新编程语言
- Swift之父-Chris Lattner
- Clang编译器的作者、LLVM项目的主要发起人
- 从Apple离职后,跳槽到Tesla、Google
- 目前(19年)在Google Brain从事AI研究
- Swift5.x版本,ABI终于稳定
- API(Application Programming Interface):应用程序编程接口
- 源代码和库之间的接口
- ABI(Application Binary Interface):应用程序二进制接口
- 应用程序与操作系统之间的底层接口
- 涉及的内容有:目标文件格式、数据类型的大小\布局\对齐、函数调用约定等等
- API(Application Programming Interface):应用程序编程接口
- 随着ABI的稳定,Swift语法不会有太大的改动
- 完全开源,主要使用 C++ 编写
三、编译流程
- 概览图
- 编译流程
-
swiftc
- swiftc存放在Xcode内部
- Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
- 一些操作
- 生成语法树:swiftc -dump-ast main.swift
- 生成最简洁的SIL代码:swiftc -emit-sil main.swift(中间代码)
- 生成LLVM IR代码:swiftc -emit-ir main.swift -o main.ll
- 生成汇编代码:swiftc -emit-assembly main.swift -o main.s
- 对汇编代码进行分析,可以真正掌握编程语言的本质
- swiftc存放在Xcode内部
四、汇编初探
只是说明了一下Swift和OC之类的底层汇编不一样
五、HelloWorld
- 一句代码即可执行
-
不用辨析main函数,Swift将全局范围内的首句可执行代码作为程序入口
-
一句代码尾部可以省略分号(;),多句代码写到同一行时必须用分号(;)隔开
-
用
var
定义变量,let
定义常量,编译器能自动推断出变量\常量的类型- 打印
- 拼接打印
-
Playground可以快速预览代码效果,是学习语法的好帮手
-
实时预览每行代码结果
-
Command + Shift + Enter:运行整个Playground
-
可以选中只运行的部分
六、Playground - View
- 还是UIKit框架
- 展示vc或view都可以
- 支持多Page
七、注释
-
支持嵌套
-
Playground的注释支持Markup语法(与markdown相似)
-
//:
开启单行Markup语法 -
/*:*/
冒号后面开启多行Markup语法 -
点击在
Edit
中的Show Render Markup
来渲染markup效果 -
Markup语法只在Playground中有效
-
支持页面切换:
[下一页](@next)
或[上一页](@previous)
八、常量
-
只能赋值1次
-
其值不要求在编译时期确定(其他编程语言一般都会要求),但使用之前必须赋值1次
- 如果不赋值,需要指定类型(推断:指定类型是在什么时期?)
- 如果不赋值,需要指定类型(推断:指定类型是在什么时期?)
-
常量、变量在初始化之前,都不能使用
九、标识符
- 标识符(比如常量名、变量名、函数名)几乎可以使用任何字符
- 标识符不能以数字开头,不能包含空白字符、制表符、箭头灯特殊字符
十、常见数据类型
-
值类型(value type):
-
枚举(
enum
):Optional
-
结构体(
struct
):Bool
、Int
、Flaot
、Double
、Character
、String
、Array
、Dictionary
、Set
定义成结构体说明可以调用方法
-
-
引用类型(reference type):
- 类(
class
)
- 类(
十一、字面量
-
布尔
let bool = true // false
-
字符串
let string = "你好"
-
字符(可存储ASCII字符、Unicode字符)
let character: Character = "🐷"
因为是双引号,如果不写
Character
,则会被认为是String
类型 -
整数
let intDeciaml = 17 // 十进制 let intBinary = 0b10001 // 二进制 let intOctal = 0o21 // 八进制 let intHexadecimal = 0x11 // 十六进制
-
浮点数
let doubleDecimal1 = 125.0 // 十进制,等价于1.25e2 let doubleDecimal2 = 0.0125 // 十进制,等价于1.25e2 let doubleHexadecimal1 = 0xFp2 // 十六进制,意味着15x2^2,相当于十进制的60.0 let doubleHexadecimal2 = 0xFp-2 // 十六进制,意味着15x2^-2,相当于十进制的3.75 //12.1875 == 1.21875e1 == 0xC.3p0
-
整数和浮点数可以添加额外的零或下划线来增强可读性
- 100_0000
- 1_000_000.000_000_1
- 000123.456
-
数组
let array = [1,3,5,7,9]
-
字典
let dictionary = ["age" : 18, "height" : 168 , "weight" : 120]
-
类型转换
不同类型不能直接相加(或其他计算\操作)
-
整数转换
let int1: UInt16 = 2_000 let int2: UInt8 = 1 let int3: int1 + UInt16(int2) // 低类型要转换成高类型,即占用空间小的类型要转换成占用空间比较大的类型
-
整数、浮点数转换
let int = 3 let double = 0.14159 let pi = Double(int) + double let intPi = Int(pi)
-
字面量可以直接相加,因为数字字面量本身没有明确的类型
let result = 3 + 0.14159
类型是给常量或变量定义的,和字面量没有关系(其实还有更深层次的原因,我猜是因为结构体)
-
可选类型(optionals)
使用可选类型optionals来处理值可能缺失的情况。可选类型表示两种可能: 或者有值, 你可以解析可选类型访问这个值, 或者根本没有值。
-
可选类型
let possibleNumber = "123" let convertedNumber = Int(possibleNumber) // convertedNumber 被推测为类型 "Int?", 或者类型 "optional Int"
因为该构造器可能会失败,所以它返回一个可选类型(optional)
Int
,而不是一个Int
。一个可选的Int
被写作Int?
而不是Int
。问号暗示包含的值是可选类型,也就是说可能包含Int
值也可能不包含值。(不能包含其他任何值比如Bool
值或者String
值。只能是Int
或者什么都没有。) -
nil
你可以给可选变量赋值为
nil
来表示它没有值:var serverResponseCode: Int? = 404 // serverResponseCode 包含一个可选的 Int 值 404 serverResponseCode = nil // serverResponseCode 现在不包含值
注意
nil
不能用于非可选的常量和变量。如果你的代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型。如果你声明一个可选常量或者变量但是没有赋值,它们会自动被设置为
nil
:var surveyAnswer: String? // surveyAnswer 被自动设置为 nil
注意
Swift 的
nil
和 Objective-C 中的nil
并不一样。在 Objective-C 中,nil
是一个指向不存在对象的指针。在 Swift 中,nil
不是指针——它是一个确定的值,用来表示值缺失。任何类型的可选状态都可以被设置为nil
,不只是对象类型。 -
空合运算符(Nil Coalescing Operator)
-
空合运算符(
a ?? b
)将对可选类型a
进行空判断,如果a
包含一个值就进行解包,否则就返回一个默认值b
。表达式a
必须是 Optional 类型。默认值b
的类型必须要和a
存储值的类型保持一致。 -
空合运算符是对以下代码的简短表达方法:
a != nil ? a! : b
上述代码使用了三元运算符。当可选类型
a
的值不为空时,进行强制解包(a!
),访问a
中的值;反之返回默认值b
。无疑空合运算符(??
)提供了一种更为优雅的方式去封装条件判断和解包两种行为,显得简洁以及更具可读性。注意
如果
a
为非空值(non-nil
),那么值b
将不会被计算。这也就是所谓的短路求值。 -
下文例子采用空合运算符,实现了在默认颜色名和可选自定义颜色名之间抉择:
let defaultColorName = "red" var userDefinedColorName: String? //默认值为 nil var colorNameToUse = userDefinedColorName ?? defaultColorName // userDefinedColorName 的值为空,所以 colorNameToUse 的值为 "red" userDefinedColorName = "green" colorNameToUse = userDefinedColorName ?? defaultColorName // userDefinedColorName 非空,因此 colorNameToUse 的值为 "green"
-
-
强制解析
当你确定可选类型确实包含值之后,你可以在可选的名字后面加一个感叹号(
!
)来获取值。这个惊叹号表示“我知道这个可选有值,请使用它。”这被称为可选值的强制解析(forced unwrapping):if convertedNumber != nil { print("convertedNumber has an integer value of \(convertedNumber!).") } // 输出“convertedNumber has an integer value of 123.”
注意
使用
!
来获取一个不存在的可选值会导致运行时错误。使用!
来强制解析值之前,一定要确定可选包含一个非nil
的值。 -
隐式解析可选类型
-
在程序架构中,第一次被赋值之后,可以确定一个可选类型总会有值。这种类型的可选状态被定义为隐式解析可选类型(implicitly unwrapped optionals)
-
把想要用作可选的类型的后面的问号(
String?
)改成感叹号(String!
)来声明一个隐式解析可选类型。 -
与其在使用时把感叹号放在可选类型的名称的后面,你可以在定义它时,直接把感叹号放在可选类型的后面。
-
当可选类型被第一次赋值之后就可以确定之后一直有值的时候,隐式解析可选类型非常有用。
-
隐式解析可选类型主要被用在 Swift 中类的构造过程中,请参考 无主引用以及隐式解析可选属性。
-
一个隐式解析可选类型其实就是一个普通的可选类型,但是可以被当做非可选类型来使用,并不需要每次都使用解析来获取可选值。
-
下面的例子展示了可选类型
String
和隐式解析可选类型String
之间的区别:let possibleString: String? = "An optional string." let forcedString: String = possibleString! // 需要感叹号来获取值 let assumedString: String! = "An implicitly unwrapped optional string." let implicitString: String = assumedString // 不需要感叹号
-
你可以把隐式解析可选类型当做普通可选类型来判断它是否包含值:
if assumedString != nil { print(assumedString!) } // 输出“An implicitly unwrapped optional string.”
-
你也可以在可选绑定中使用隐式解析可选类型来检查并解析它的值:
if let definiteString = assumedString { print(definiteString) } // 输出“An implicitly unwrapped optional string.”
注意
如果一个变量之后可能变成
nil
的话请不要使用隐式解析可选类型。如果你需要在变量的生命周期中判断是否是nil
的话,请使用普通可选类型。 -
十二、元组(tuple)
let http404Error = (404, "Not Found")
print("The status code is \(http404Error.0)")
let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")
let (justTheStatusCode,_) = http404Error
let http200Status = (statusCode: 200, description: "OK")
print(The status code is \(http200Status.statusCode))
十三、流程控制
1、if-else
let age = 4
if age >= 22
{
print("Get married")
}
else if age >= 18
{
print("Being a adult")
}
else if age >= 7
{
print("Go to school")
}
else
{
print("Just a child")
}
if
后面的条件可以省略小括号- 条件后面的大括号不可以省略
if
后面的条件只能是Bool类型(不能是int
或nil
等)
2、while
var num = 5
while num > 0
{
print("num is \(num)")
num -= 1
} // 打印了5次
-
同样小括号可以省略,大括号不能省略
-
为什么是
num -= 1
而不是num--
?从Swift3开始,去除了自增(++)、自减(--)运算符,因为不易阅读
var num = -1
repeat
{
print("num is \(num)")
} while num > 0 // 打印了1次
repeat-while
相当于C语言中的do-while
3、for
-
闭区间运算符:
a...b
,即:a <= 取值 <= b
let names = ["Anna", "Alex", "Brian", "Jack"] for i in 0...3 { print(names[i]) } // Anna Alex Brian Jack
let range = 1...3 for i in range { print(names[i]) } // Alex Brian Jack
let a = 1 var b = 2 for i in a...b { print(names[i]) } // Alex Brian for i in a...3 { print(names[i]) } // Alex Brian Jack
// i默认是let,有需要时可以声明为var for var i in 1...3 { i += 5 print(i) } // 6 7 8
i
默认是let
for _ in 1...3 { print("for") } // 打印了3次
-
半开区间运算符:
a..<b
,即:a <= 取值 < b
-
for
- 区间运算符用在数组上let names = ["Anna", "Alex", "Brian", "Jack"] for name in names[0...3] { print(name) } // Anna Alex Brian Jack
-
单侧区间:让区间朝一个方向尽可能的远
for name in names[2...] { print(name) } // Brian Jack for name in names[...2] { print(name) } // Anna Alex Brian for name in names[..<2] { print(name) } // Anna Alex
let range = ...5 range.contains(7) // false range.contains(4) // true range.contains(-3) // true
-
区间类型
let range1: ClosedRange<Int> = 1...3 let range2: Range<Int> = 1..<3 let range3: PartialRangeThrough<Int> = ...5
-
字符、字符串也能使用区间运算符,但默认不能用在
for-in
中:// 错误举例: for _ in "a"..."z" { print("123") } // 会报错
但是可以单独拿出来用:
let stringRange1 = "cc"..."ff" // ClosedRange<String> stringRange1.contains("cb") // false stringRange1.contains("dz") // true stringRange1.contains("fg") // false let stringRange2 = "a"..."f" // 此处仍是字符串ClosedRange<String> stringRange2.contains("d") // true stringRange2.contains("h") // false
// \0到~囊括了所有可能要用到的ASCII字符 let characterRange: ClosedRange<Character> = "\0"..."~" characterRaneg.contains("G") // true
-
带间隔的区间值 - stride
let hours = 11 let hourInterval = 2 // tickMark的取值:从4开始,累加2,不超过11 for tickMark in stride (from: 4, through: hours, by: hourInterval) { print(tickMark) } // 4 6 8 10
4、switch
var number = 1
switch number
{
case 1:
print("number is 1")
break
case 2:
print("number is 2")
break
default:
print("number is other")
break
}
-
case
、default
后面不能写大括号{}
-
默认可以不写
break
,并不会贯穿到后面的条件 -
使用
fallthrough
可以实现贯穿效果 -
switch
必须要保证能处理所有情况 -
case
、default
后面至少有一条语句如果不想做任何事,加个
break
即可 -
如果能保证已处理所有情况,也可以不必使用
default
-
复合条件
switch
也支持Character、String类型
- 不需要
fallthrough
,用,
也可以
-
区间匹配、元组匹配
-
下划线
_
代表:忽略某个值 -
关于
case
匹配问题,属于模式匹配(Pattern Matching)的范畴,以后会再次详细展开讲解 -
值绑定
-
带有值绑定,只需要一方匹配
-
必要时
let
也可以改为var
-
where
-
在
for
循环中作用类似oc的continue
-
标签语句