一、Swift发展史
- 2014.8 1.0
- 2015.9 2.0
- 2016.9 3.0
- 2017.9 4.0
- 2019.3 5.0
Swift 2.0
- Error handing 增强
- guard语法
- 协议支持扩展
Swift 3.0
- 新的GCD和Core Graphics
- NS前缀从老的Foundation类型中移除
- 内联序列函数sequence
- 新增fileprivate和open两个权限控制
- 移除了诸多弃用的特性,比如++、--运算符
Swift 4.0
- extension中可以访问private的属性
- 类型和协议的组合类型
- Associated Type可以追加Where约束语句
- 新的Key Paths语法
- 下标支持泛型
- 字符串增强
Swift 5.0
- ABI稳定
- Raw strings
- 标准库新增Result
- 定义了与Python或Ruby等脚本语言互操作的动态可调用类型
二、Swift和OC的比较
编程范式
- Swift可以面向协议编程、函数式编程、面向对象编程
- OC以面向对象编程为主,当然你可以引入类似RAC的类库来进行函数式编程
类型安全
- Swift是一门类型安全的语言。鼓励程序员在代码中清除明确值的类型。如果代码中使用一个字符串String,那么你不能错误地传递一个整形Int给它。因为Swift是类型安全的,它会在编译的时候做类型检查,并且把所有不匹配的类型作为一个错误标记出来。这样使得程序员在开发中尽可能早地发现和修正错误。
- 而OC则不然,你声明一个NSString变量,仍然可以传一个NSNumber给它,尽管编译器抱怨,但是你仍然可以作为NSNumber来使用它。
值类型增强
- 在Swift中,典型的有struct、enum以及tuple都是值类型。而平时使用的Int、Double、Float、String、Array、Dictionary、Set其实都是用结构体实现的,也是值类型。
- OC中,NSNumber、NSString以及集合类对象都是指针类型。
枚举增强
- Swift的枚举可以使用整形、浮点型、字符串等,还能拥有属性和方法,甚至支持泛型、协议、扩展等等。
- OC里面的枚举则鸡肋很多
泛型
- Swift支持泛型,也支持泛型的类型约束等特性
- 苹果推出了Swift2.0版本,为了让开发者从OC更好地过渡到Swift上,苹果也为OC带来了generics泛型支持,不过OC的泛型约束也仅停留在编译器警告阶段。
协议和扩展
- Swift对协议的支持更加丰富,配合扩展、泛型、关联类型等可以实现面向协议编程,从而大大提高代码的灵活性。同时,Swift中的protocol还可以用于值类型,如结构体和枚举
- OC的协议缺乏强约束,提供的optional特性往往成为很多问题的来源,而如果放弃optional又会让实现代价过大
函数和闭包
- Swift中函数是一等公民,可以直接定义函数类型变量,可以作为其它函数参数传递,可以作为函数返回值返回
- OC里面函数仍然是次等公民,需要selector封装或者使用block才能模拟Swift中类似的效果
三、swiftc--强大的命令行工具
swiftc常用命令
- 生成可执行文件
swiftc -0 main.out main.swift
- Swift Abstract Syntax Tree(AST)
swiftc main.swift -dump-ast
- Swift INtermediate Language(SIL)
swiftc main.swift -emit-sil
- LLVM Intermediate Repressentation(LLVM IR)
swiftc main.swift -emit-ir
- Assembly Language
swiftc main.swift -emit-assembly
四、REPL
- Xcode 6.1 引入了另外一种以交互式的方式来体验Swift的方法
- Read Eval PrintLoop,简称REPL
swift build Build Swift packages
swift package Create and work on packages
swift run Run a program from a package
swift test Run package tests
swift repl Experiment with Swift code interactively
操作:
- 命令行中,输入
swift repl
即可进入REPL - 退出
:quit
- 帮助
:help
- 将光标移动到当前行的开始处
control+A
- 将光标移动到当前行的结束处
control+E
五、Playground
- Swift Playground首次公布于WWDC2016
- 最开始是为了让人人都能愉快的学习Swift编程
- 但发展至今,这个工具越来越强大
- iPad APP Playgrounds
六、声明变量和常量
- 使用
let
来声明常量 - 使用
var
来声明变量
类型标注
- 在声明一个变量或常量的时候提供类型标注,来明确变量或常量能够储存值的类型
- 添加类型标注的方法是在变量或常量的名字后面加一个冒号,再跟一个空格,最后加上要使用的类型名称
- 可以在一行中定义多个相关的变量为相同的类型,用逗号隔开,只要在最后的变量名字后边加上类型标注
let a,b,c : Int
let x = 1,y = 1.1,z = "zzz"
a = 10
b = 11
c = 12
print("a = \(a),b = \(b),c = \(c)")
print("x = \(x),y = \(y),z = \(z)")
/*
a = 10,b = 11,c = 12
x = 1,y = 1.1,z = zzz
*/
OC中定义常量常用方法:1.宏定义 2.const 3.类中属性设置为readonly(如果继承重写会失效)
常量和变量命名
- 常量和变量的名字几乎可以使用任何字符,甚至包括Unicode字符
- 变量和常量的名字不能包含空白字符、数学符号、箭头、保留的(或者无效的)Unicode码位、连线和制表符。也不能以数字开头,尽管数字几乎可以使用在名字其它的地方。
打印print 和字符串插值
使用\(插值)
添加字符串插值,如"a\(b)d"
七、Swift中的数值类型
整数
- Swift提供了8、16 、 32 、64位编码的有符号和无符号整数
- 命名方式:例如8位无符号整数的类型是UInt8,32位有符号整数的类型是Int32
- 通过min和max属性来访问每个证书类型的最小值和最大值
OC中有char、short、int、long、NSInteger类型,分别对应8 、 16 、32 、 64位整形,无符号类型一般为在前面加上unsigned
Swift中的Int对应于OC的NSInteger,在32位CPU为32位,在64位CPU为64位
浮点类型
- Double:64位浮点数,至少有15位数字的精度
- Float:32位浮点数,至少有6位数字的精度
- 在两种类型都可以的情况下,推荐使用Double类型
OC中除了有来自于C的float、double外,还有CGFloat,CGFloat在32位CPU为32位,在64位CPU为64位
Bool
- Bool: 只有两个值,true或false
- Swift的类型安全机制会阻止你用一个非布尔量的值替换掉Bool
OC中有0为假,非0为真的概念,Swift中没有
类型别名
- 类型别名是为一个已存在的类型定义一个可选择的名字
- 使用关键字typealias定义一个类型的别名
- 当你想通过在一个在上下文中看起来更合适可具有表达性的名字来引用一个已存在的类型时,这时别名就非常有用了
// 声音采样
typealias AudioSample = UInt8
let sample: AudioSample = 32
八、元组(Tuple)
- 元组可以把多个值合并成单一的复合型的值
- 元组内的值可以是任何类型,而且可以不必是同一个类型
元素命名
- 元组中的每个元素可以指定对应的元素名称
- 如果没有指定名称的元素也可以使用下标的方式来引用
let error = (statuCode: 404, errorMsg: "服务器在这个路径找不到指定资源")
print(error.statuCode) //404
print(error.1) //服务器在这个路径找不到指定资源
Tuple修改
- 用var定义的元组就是可变元组,let定义的元组就是不可变元组
- 不管是可变还是不可变元组,元组在创建后就不能增加和删除元素
- 可以对可变元素的元素值进行修改,但是不能改变其类型
- any类型的值可以赋任何类型值
Tuple分解
- 可以将一个元组的内容分解成单独的常量或变量
- 如果只需要使用其中的一部分数据,不需要的数据可以用下划线
_
代替
let error = (404, errorMsg: "服务器在这个路径找不到指定资源")
let (statuCode,errorMsg) = error
print(statuCode)
print(errorMsg)
let (_,errorMsg1) = error
print(errorMsg1)
九、Optional的使用
为什么需要Optional
- OC里的nil是无类型的指针
- OC里面的数组、字典、集合等不允许放入nil
- OC所有对象变量都可以为nil
- OC只能用在对象上,而在其他地方又用其他特殊值(例如NSNotFound)表示值的缺失
Optional
可以通过给可选变量赋值一个nil来将之设置为没有值
- 在OC中nil是一个指向不存在对象的指针
- 在Swift中,nil不是指针,它是值缺失的一种特殊类型,任何类型的可选项都可以设置成nil而不仅仅是对象类型
Optional-If语句以及强制展开
- 可选项是没法直接使用的
- 需要用!展开之后才能使用(意思是我知道这个可选项里面肯定有值,展开吧),如果使用了!却没有值,会抛出异常崩溃
- 可以用if 来可选绑定接收的方式使用
let a: Int?
//a!//expression resolves to an unused variable a!
a = 10
if let a = a{
print(a)
}
Optional-绑定
- 可以使用可选绑定来判断可选项是否包含值,如果包含就把值赋给一个临时的常量或者变量
- 可选绑定可以与if和while的语句使用来检查可选项内部的值,并赋值给一个变量或常量
- 同一个if语句中包含多可选项绑定,用逗号分隔即可。如果任一可选绑定结果是nil或者布尔值为false,那么整个if判断会被看做else
Optional-隐式展开
- 有些可选项一旦被设定值之后,就会一直拥有值,在这种情况下,就可以去掉检查的需要,也不必每次访问的时候都进行展开
- 通过在声明的类型后边添加一个叹号(!)而非问号(?)来书写隐式展开可选项
- 隐式展开可选项主要被用在Swift类的初始化过程中
Optional-可选链
- 可选项后面加问号
- 如果可选项不为nil,返回一个可选项结果,否则返回nil
let a: String? = "abcdef"
let count = a?.count
if let count = count{
let lastIndex = count - 1
print(lastIndex) // 5
}
十、Optional的原理
Optional-实现探究
- Optional其实是标准库里的一个enum类型
- 数据值其实是Optional枚举的关联值
- 用标准库实现语言特性典型
public enum Optional<Wrapped> : ExpressibleByNilLiteral {
case none
case some(Wrapped)
@inlinable public var unsafelyUnwrapped: Wrapped { get }
}
- Optional.none就是nil
- Optional.some则包装了实际的值
// 使用Optional枚举来定义可选值
// 和使用String?定义的一模一样
let a: Optional<String> = "abcdef"
- 泛型属性
unsafelyUnwrapped
,理论上我们可以直接调用unsafelyUnwrapped获取可选项的值
let a: Optional<String> = "abcdef"
let count = a.unsafelyUnwrapped.count
print(count) // 6
十一、创建和初始化字符串
初始化字符串
- 字面量
- 初始化器语法
- isEmpty检查是否为空串
var emptyString = ""
var anotherEmptyString = String()
if (emptyString.isEmpty){
print("这个字符串是空的")
}
字面量
- 字符串字面量是被双引号("")包裹的固定顺序文本字符
- Swift会为str常量推断类型为String
let str = "a string"
多行字面量
- 多行字符串字面量是用三个双引号引起来的一系列字符
- 多行字符串字面量把所有行包括在引号内,开始和结束默认不会有换行符
- 当你的代码中在多行字符串字面量里包含了换行,那个换行符同样会成为字符串里的值。如果你想要使用
换行符
来让你的代码易读,却不想让换行符成为字符串的值,那就在那些行的末尾使用反斜杠(\)
- 要让多行字符串字面量起始或结束于换行,就在第一或最后一行写一个空行
- 多行字符串可以缩进以匹配周围的代码。双引号(""")前的空格会告诉Swift其他行前应该有多少空白是需要忽略的
- 如果你在某行的空格超过了结束的双引号("""),那么这些空格会被包含
字符串里的特殊字符
- 转义特殊字符
\0
(空字符),\\
(反斜杠),\t
(水平制表符),\n
(换行符),\r
(回车符),\"
(双引号),\'
(单引号) - 任意的unicode标量,写作
\u{n}
,里面的n是一个1-8位的16进制数字,其值是合法unicode值 - 可以在多行字符串字面量中包含双引号(")而不需要转义。要在多行字符串中包含文本""",转义至少一个双引号
扩展字符串分隔符(Raw String)
- 在字符串字面量中放置扩展分隔符来在字符串中包含特殊字符而不让它们真的生效
- 把字符串放在双引号(")内并由井号(#)包裹
- 如果字符串里有"#则首尾需要两个##
- 如果你需要字符串中某个特殊符号的效果,使用匹配你包裹的井号数量的井号并在前面写转义符号
\
let str = #"Line 1\nLiine 2"#
let str1 = ##"Line 1\#nLiine 2"##
let str2 = ###"Line 1\###nLiine 2"###
print(str)
print(str1)
print(str2)
/*
Line 1\nLiine 2
Line 1\#nLiine 2
Line 1
Liine 2
*/
十二、字符串的常见操作
字符串的可变性
- var 指定的可以修改
- let 指定的不可修改
- 对比OC(NSString和NSMutableString)
字符串是值类型
- String值在传递给方法或者函数的时候会被复制过去
- 赋值给常量或者变量的时候也是一样
- Swift编译器优化了字符串使用的资源,实际上拷贝只会在确实需要的时候才进行
操作字符
-
for-in
循环遍历String中的每一个独立的Character - Character类型
- String值可以通过传入Character数组来构造
字符串插值
- 字符串插值是一种从混合常量、变量、字面量和表达式的字符串字面量构造新String值的方法
- 每一个你插入到字符串字面量的元素都要被一对圆括号包裹,然后使用反斜杠前缀
- 类似于NSString的
stringWithFormat
方法,但是更加简便,更强大 - 可以在扩展字符串分隔符中创建一个包含在其他情况下会被当做字符串插值的字符
- 要在使用扩展分隔符的字符串中使用字符串插值,在反斜杠后使用匹配首尾井号数量的井号
print(#"Write a interpolated string in Swift using \(multiplier)."#)
print(#"6 times 7 is \#(6 * 7)."#)
/*
Write a interpolated string in Swift using \(multiplier).
6 times 7 is 42.
*/
十三、如何使用索引访问和修改字符串
字符串索引
- 每一个String值都有相关的索引类型,即
String.Index
。它相当于每个Character在字符串中的位置 - startIndex属性来访问String中第一个Character的位置。endIndex属性就是String中最后一个字符后的位置
- endIndex属性并不是字符串下标脚本的合法实际参数
- 如果String为空,则startIndex和endIndex相等
- 使用
index(before:)
和index(after:)
方法来访问给定索引的前后 - 要访问给定索引更远的索引,你可以使用
index(_:offsetBy:)
* 使用indices属性来访问字符串中每个字符的索引
let string = "abcdefg"
print(string[string.startIndex]) // a
//print(string[1])//报错
print(string[string.index(before:string.endIndex)]) // g
print(string[string.index(after:string.startIndex)]) // b
print(string[string.index(string.startIndex,offsetBy:3)]) // d
print(string[string.index(string.endIndex,offsetBy:-3)]) // e
插入
- 插入字符,使用
insert(_:at:)
方法 - 插入另一个字符串的内容到特定的索引,使用
insert(contentsOf:at:)
方法
var string = "abcdefg"
string.insert("x",at:string.index(string.startIndex,offsetBy:2))
print(string)//abxcdefg
string.insert(contentsOf:"12345",at:string.index(string.endIndex,offsetBy:-2))
print(string)//abxcde12345fg
删除
- 移除字符,使用
remove(at:)
方法 - 移除一小段特定范围的字符串,使用
removeSubrange(_:)
方法
var string = "abcdefg"
string.remove(at: string.index(string.startIndex,offsetBy:2))
print(string)//abdefg
string.removeSubrange(string.index(string.endIndex,offsetBy:-4)..<string.index(string.endIndex,offsetBy:-2))
print(string)//abfg
十四、子字符串、字符串比较
子字符串
- 使用下标或者类似
prefix(_:
的方法得到的子字符串是Substring类型(注意这里string的s是小写) - Substring拥有String的大部分方法
- Substring可以通过String的构造器转成String类型
let string = "abcdefg"
let index = string.index(of: "e") ?? string.endIndex
let beginning = string[..<index]
let newString = String(beginning)
print(beginning) // abcd
print(beginning is String) // false
print(beginning is Substring) // true
print(newString) // abcd
print(newString is String) // true
print(newString is Substring) // false
- 子字符串重用一部分原字符串的内存
- 修改字符串或者子字符串之前都不需要花费拷贝内存的代价
- String和Substring都遵循StringProtocol协议,也就是说它基本上能很方便地兼容所有接受StringProtocol值的字符串操作函数
字符串比较
- 字符串和字符想等性(==和!=)
- 前缀相等性
hasPrefix(_:)
- 后缀相等性
hasSuffix(_:)
let string = "abcdefg"
let string1 = "abcdefghijk"
print(string == string1) // false
print(string.hasPrefix("abcd")) // true
print(string1.hasPrefix("abcd")) // true
print(string.hasSuffix("efg")) // true
print(string1.hasSuffix("hijk")) // true