常见数据类型
引用类型:类class 值类型:枚举enum、结构体struct 结构体下分为 Bool、Int、Float、Double、Character、String、Array、Dictionary、Set
参数标签(Argument Label)
可以修改参数标签
func goToWork(at time:String){
print("this time is \(time)")
}
goToWork(at:"08:00") // this time is 08:00
可以使用下划线_省略参数标签
func sum(_ v1: Int, _ v2:Int) -> Int {
v1 + v2
}
sum(10,20)
输入输出参数inout
var number = 10
func add(_ num:inout Int){
num = 20
}
add(&number)
函数类型(Function Type)
- 每一个函数都是有类型的,函数类型由形式参数类型,返回值类型组成
func test(){} // () -> Void 或者 () - ()
func sum(a:Int,b:Int) -> Int {
a + b
}// (Int,Int) -> Int
var fn:(Int,Int) -> Int = sum
fn(2,3) // 5,调用时不需要参数标签
函数类型作为函数参数
func sum(v1:Int,v2:Int) -> Int {
v1 + v2
}
func difference(v1:Int,v2:Int) -> Int {
v1 - v2
}
func printResult(_ mathFn:(Int,Int) -> Int,_ a:Int,_ b:Int){
print("Result:\(mathFn(a,b))")
}
printResult(sum,5,2) // Result:7
printResult(difference,5,2) // Result:3
函数类型作为作为函数返回值
func next(_ input:Int) -> Int {
input + 1
}
func previous(_ input:Int) -> Int {
input - 1
}
func forward(_ forward:Bool) -> (Int) -> Int {
forward ? next : previous
}
forward(true)(3) // 4
forward(false)(3) // 2
枚举
enum Direction {
case north
case south
case east
case west
}
enum Direction {
case north,south,east,west
}
var dir = Direction.west
dir = Direction.east
dir = .noth
print(dir) // north
switch dir {
case .north:print("north")
case .south:print("south")
case .east:print("east")
case .west:print("west")
}
关联值(Associated Values)
- 有时会将枚举的成员值跟其他类型的关联存储在一起,会非常有用
enum Score {
case points(Int)
case grade(Character)
}
var score = Score.points(96)
score = .grade("A")
原始值(Raw Values)
- 枚举成员可以使用相同类型的默认值预先关联,这个默认值叫做:原始值
enum Grade :String {
case perfect = "A"
case great = "B"
case good = "C"
case bad = "D"
}
var grade = Grade.perfect
print(grade) //perfect
print(grade.rawValue) // A
print(Grade.perfect.rawValue) //A
隐式原始值(Implicitly Assigned Raw Values)
- 如果枚举的原始值类型是Int、String,Swift会自动分配原始值
enum Direction {
case north
case south
case east
case west
}
//等价于
enum Direction {
case north,south,east,west
}
print(Direction.north) // north
print(Direction.north.rawValue) // north
enum Season : Int {
case sprint = 1,summer,autumn = 4,winter
}
print(Season.spring.rawValue) // 1
print(Season.summer.rawValue) // 2
print(Season.autumn.rawValue) // 4
print(Season.winter.rawValue) // 5
可选项(Optional)
可选项,一般也叫可选类型,它允许将值设置为nil。 在类型名称后面加个问号?来定义一个可选项
var name:String? = "Jack"
name = nil
强制解包(Forced Unwrapping)
- 可选项是对其他类型的一层包装,可以将它理解为一个盒子
- 如果为nil,那么它是个空盒子
- 如果不为nil,那么盒子里装的是:被包装类型的数据
var age:Int?// 默认就是nil
age = 10
age = nil
- 如果要从可选项中取出被包装的数据(将盒子里的装的东西取出来),需要使用感叹号!进行强制解包
var age:Int? = 10
let ageInt:Int = age!
ageInt += 10
- 如果对值为nil的可选项(空盒子)进行强制解包,将会产生运行时错误
判断可选项是否包含值
let number = Int("123") // 返回是可选类型
if number != nil {
print(“字符串”)
}else {
print("字符串转换整数失败")
}
可选绑定(Optional Binding)
- 可以使用可选项绑定来判断可选项是否包含值
- 如果包含就自动解包,把值赋给一个临时的常量年(let)或者变量(var),并返回true,否则返回false
if let number = Int("123") {
print("字符串转换整数成功:\(number)")
} else {
print("字符串转换整数失败")
}
enum Season:Int {
case spring = 1,summer,autumn,winter
}
if let season = Season(rowValue:6) {
switch season {
case .spring : print("the season is spring")
default:print("the season is other")
}
}else {
print("no such season")
}
空合并运算符 ??
a ?? b ( a 不为 空,就返回 a, 为空就返回 b,但是重要一点是 如果 b 不为可选项,a 不为空,那么返回的是 a 解包后的值) a 是可选项 b 是可选项或者 不是可选项 b 跟 a 的存储类型必须相同 如果a 不为nil,就返回a 如果b 不是可选项,返回a时会自动解包
let a: Int? = 1
let b: Int? = 2
let c = a ?? b // c 是Int?,Optional(1)
let a: Int? = nil
let b: Int? = 2
let c = a ?? b // c 是Int?,Optional(2)
let a: Int? = nil
let b: Int? = nil
let c = a ?? b // c 是Int?,nil
guard 语句
guard 条件 else{ //do something 退出当前作用域 // return、break、continue、throw、error }
guard 语句的条件为false时,就会执行大括号里面的代码 guard 语句的条件为true时,就会跳过guard语句 guard 语句特别适合用来“提前退出”
当使用guard语句进行可选项绑定时,绑定的常量(let)、变量(var)也能在外层作用域中使用
function login (_ info:[String,String]){
guard let username = info["username"] else {
print('请输入用户名')
return
}
guard let password = info["password"] else {
print('请输入密码')
return
}
// if username...
// if password...
print('用户名:\(username)',"密码:\(password)",'登陆ing')
}
隐式解包(Implicitly Unwarapped Optional)(尽量不使用)
- 在某些情况下,可选项一旦被设定值之后,就会一直拥有值
- 在这种情况下, 可以去掉检查,也不必每次访问的时候都进行解包,因为它能确定每次访问的时候都有值
- 可以在类型后面加个!,定义一个隐式解包的可选项
简单说,如果这个可选项有值的话,可以用!设置可选项,在赋值给其他的变量时,会隐式解包(就是在强制解包的基础上,隐藏了!)
let num1:Int! = 10
let num2:Int = num
if num1 != nil {
print(num1 + 6)
}
if let num3 = num1 {
print(num3)
}
let num1:Int! = nil
let num2:Int = num1 // 会报错,是空值不能解包
字符串插值
- 可选项在字符串插值或者直接打印时,编译器会发出警告
var age:Int? = 10
print("My age is \(age)")
至少有3种方法消除警告
print("My age is \(age!)")
print("My age is \(String(descibing:age))")
print("My age is \(age ?? 0)")
多重可选项
var num1:Int? = 10
var num2:Int?? = num1
var num3:Int?? = 10
print(num2 == num3) // true
var num1:Int? = nil
var num2:Int?? = num1
var num3:Int?? = nil
print(num2 == num3) // false
结构体和类
- 在swift标准库中,绝大多数的公开类型都是结构体,而枚举和类只占很小一部分。
- 比如Bool、Int、Double、String、Array、Dictionary等常见类型都是结构体
- 所有的结构体都有一个编译器自动生成的初始化器(initializer,初始化方法,构造器,构造方法)
结构体的初始化器
- 会为结构体生成多个初始化器,宗旨:保证所有成员都有初始值
struct Point {
var x:Int
var y:Int
}
var p1 = Point(x:10,y:10)
var p2 = Point(y:10) // 报错
var p3 = Point(x:10) // 报错
var p4 = Point() // 报错
struct Point {
var x:Int = 0
var y:Int
}
var p1 = Point(x:10,y:10)
var p2 = Point(y:10)
var p3 = Point(x:10) // 报错
var p4 = Point() // 报错
struct Point {
var x:Int
var y:Int = 0
}
var p1 = Point(x:10,y:10)
var p2 = Point(y:10) // 报错
var p3 = Point(x:10)
var p4 = Point() // 报错
struct Point {
// 给成员变量赋值,四种初始化都是可以的
var x:Int = 0
var y:Int = 0
}
var p1 = Point(x:10,y:10)
var p2 = Point(y:10)
var p3 = Point(x:10)
var p4 = Point()
下面代码能编译通过么?
- 可选项都有个默认值nil,可以编译通过
struct Point {
// 给成员变量赋值nil,四种初始化都是可以的
var x:Int?
var y:Int?
}
var p1 = Point(x:10,y:10)
var p2 = Point(y:10)
var p3 = Point(x:10)
var p4 = Point()
自定义初始化器
- 一旦在定义结构体时自定义了初始化器,编译器就不会再帮它自动生成其他初始化器
struct Point {
var x:Int = 0
var y:Int = 0
init(x:Int,y:Int){
self.x = x
self.y = y
}
}
var p1 = Point(x:10,y:10)
var p2 = Point(y:10) // 报错
var p3 = Point(x:10) // 报错
var p4 = Point() // 报错
窥探初始化器的本质
- 以下2段代码完全等效
struct Point {
var x:Int = 0
var y:Int = 0
}
var p = Point()
struct Point {
var x:Int
var y:Int
init(){
self.x = 0
self.y = 0
}
}
var p = Point()
类(class)
- 类的定义和结构体类似,但编译器并没有为类自动生成可以传入成员值的初始化器
- 如果类的所有成员都在定义的时候指定了初始值,编译器会为类生成无参的初始化器
- 成员的初始化是在这个初始化器中完成的
class Point {
var x:Int = 0
var y:Int = 0
}
let p1 = Point()
let p2 = Point(x:10,y:20) // 报错,不会自动生成可以传入成员值的初始化器
let p3 = Point(x:10) //报错,不会自动生成可以传入成员值的初始化器
let p4 = Point(y:20) //报错,不会自动生成可以传入成员值的初始化器
- 如果类的成员没有指定值,连无参的初始化器都不会生成
class Point {
var x:Int
var y:Int
}
let p1 = Point()
类和结构体的本质区别
- 结构体是值类型(枚举也是值类型),类是引用类型(指针类型)
值类型
- 值类型赋值给var、let或者给函数传参,是直接将所有的内容拷贝一份
- 类似于对文件进行copy、past操作,产生了全新的文件副本。属于深拷贝(deep copy)
引用类型
- 引用赋值给var、let或者给函数传参,是将内存地址拷贝一份,属于浅拷贝
值类型、引用类型的let
struct Point {
var x:Int
var y:Int
}
class Size {
var width:Int
var height:Int
init(width:Int,height:Int){
self.width = width
self.height = height
}
}
let p = Point(x:10,y:20)
p = Point(x:11,y:22) // 值类型常量不可修改 ,报错
p.x = 33 // 值类型常量里面的成员变量也不可修改,报错
p.y = 44 // 值类型常量里面的成员变量也不可修改,报错
let s = Size(width:10,height:20)
s = Size(width:11,height:20) // 常量不可修改,报错
s.width = 33 // 常量里的属性是可以的
s.height = 44 // 常量里的属性是可以的
类、结构体、枚举都可以定义方法