Swift 数据类型&&方法&& 枚举、结构体、类

97 阅读9分钟

常见数据类型

引用类型:类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)

  • 可选项是对其他类型的一层包装,可以将它理解为一个盒子
  1. 如果为nil,那么它是个空盒子
  2. 如果不为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)

  • 可以使用可选项绑定来判断可选项是否包含值
  1. 如果包含就自动解包,把值赋给一个临时的常量年(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标准库中,绝大多数的公开类型都是结构体,而枚举和类只占很小一部分。
  1. 比如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() // 报错

image.png

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() // 报错

image.png

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() // 报错

image.png

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() // 报错

image.png

窥探初始化器的本质

  • 以下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)

  • 类的定义和结构体类似,但编译器并没有为类自动生成可以传入成员值的初始化器
  • 如果类的所有成员都在定义的时候指定了初始值,编译器会为类生成无参的初始化器
  1. 成员的初始化是在这个初始化器中完成的
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) //报错,不会自动生成可以传入成员值的初始化器

image.png

  • 如果类的成员没有指定值,连无参的初始化器都不会生成
class Point {
    var x:Int
    var y:Int
}

let p1 = Point()

image.png

类和结构体的本质区别

  • 结构体是值类型(枚举也是值类型),类是引用类型(指针类型)

值类型

  • 值类型赋值给var、let或者给函数传参,是直接将所有的内容拷贝一份
  1. 类似于对文件进行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 // 常量里的属性是可以的

类、结构体、枚举都可以定义方法