Swift 基础

88 阅读6分钟

常见的操作符

  • optional:var a:Int?

  • 解包:```var value:Int

if(a != nil){

value = a!} else {

value = 0

}```

  • 合并空运算符: value = a ?? 0

  • 区间运算符: var range = 0...10 var range2 = 0..<10. "..." 或者 "..<"

  • 检查某个数字是否在这个 range 中:~=, range ~= 8: range 中是否包含8这个数字

流程控制

//switch, 不需要通过 break 跳出,匹配到一个之后,会自动停止匹配
var charactor = "a"
switch (charactor)
{
    case "a":
    print("charator is a")
    default:
    break
}
//如果匹配之后,要再次匹配可以用 fallthrough
switch (charactor)
{
    case "a":
    print("charator is a")
    fallthrough
    case "b":
    print("charator is b")
    fallthrough
    case "c":
    print("charator is c")
    default:
    break
}

var tuple = (1,1)
switch (tuple)
{
    case (let a, 1):
    print(a)
    case (let b, 0):
    print(b)
    case (let a, let b):
    print(a,b)
}
switch (tuple)
{
    case (let a, 1) where a == 1:
    print(a)
    case (let b, 0):
    print(b)
    case (let a, let b):
    print(a,b)
}
//continue
MyLabel:for _ in 0...10{
    for _ in 0...10{
    for index3 in 0...10{
    if (index3 == 6){
    continue MyLabel
}
print(index3)
}}}

//guard else
func myFunc(param:Int)->Int{
    guard param > 0 else{
return 0
}
print("do something else")
return 1
}

函数和闭包

  • 函数结构:

func method( param1,param2,...)->return value{实现部分}

  • 返回 optional 类型
func search2(dataId:Int)->Int?
{
    guard dataId > 100 else {
    return nil
}
    return dataId-100
}
  • 匿名变量
func myFunc2(_ param1:Int, _ param2:Int)->Int{
    return param1 + param2
}
var ret = myFunc2(1, 2)
  • 可变参数: 形参会被包装成一个集合类型赋值给对应的参数,但是传递的参数类型必须相同,并且可以传递多组数量可变的参数,不同参数之间参数类型可以不同
func myFunc3(param:Int...)->Int
{
    var sum = 0
    for value in param{
        sum += value
}
return sum
}

myFunc3(param: 1,2,3,4,5)
//多参数
func myFunc4(param1:Int..., param2:String...)
{
    for value in param1{
    print(value)
}
for value in param2{
    print(value)
}
}
myFunc4(param1: 1,2,2,3, param2: "hi","你好")
  • 注意,如果传递的是值类型的参数,那么参数值在传递进函数内部时会将原值复制为一个常量,且在函数内部不可修改,“基本数据类型,枚举,和结构体都是属于值类型”

  • 如果真的需要在函数内部修改传递参数的变量值,可以将此参数声明为inout类型,在传递参数的时候需要加上&符号,这个符号传递参数变量的内存地址

  • 函数变量: 下列代码通过闭包的方式对addFunc进行了赋值

var addFunc:(Int, Int)->Int
addFunc = {(param1:Int, param2:Int) in return param1 + param2}
addFunc(2,3)
  • 闭包实质上是一段有具体功能的代码块,结构为{(param1, param2, ...)in 代码块}, 如果代码块里面有返回值,就可以省略返回类型,可以推导返回值类型

  • 函数作为入参

var plusFunc:(Int, Int)->Int
func myFunc6(param1:Int, param2:Int)->Int{
    return param1 + param2
}
plusFunc = myFunc6
plusFunc(2,3)
func printFunc(param:(Int, Int)->Int, param1:Int, param2:Int)
{
    print(param(param1,param2))
}
printFunc(param: plusFunc, param1: 4, param2: 5)
  • 嵌套函数: 这种嵌套函数也有作用域
func myFunc8()->(Int,Int)->Int{
    func subFunc(param1:Int, param2:Int)->Int{
        return param1 + param2
}
    return subFunc
}
  • 函数作为参数,有以下例子
func mySort(arry:inout Array<Any>, sortClosure:(Any,Any)->Bool)->Array<Any>{
    for indexI in arry.indices {
        if indexI == arry.count - 1{
    break
}
    for indexJ in 0...((arry.count - 1)-indexI-1){
        if sortClosure(arry[indexJ], arry[indexJ + 1])
        {
        }
        else
        {
            arry.swapAt(indexJ, indexJ+1)
        }
    }
}
return arry
}
mySort(arry: &array) { (i:Any, j:Any) in
    return (i as! Int) < (j as! Int)
}
print(array)
  • as!类型强转, 当闭包的代码块只有一行代码,也可以省略return

  • 后置闭包,当函数的最后一个参数为闭包参数,在调用函数时,可以将闭包结构脱离出函数的参数列表,追加在函数的尾部,增加代码可读性


mySort(arry: &array){
    ($0 as! Int) > ($1 as! Int)
}

//如果只有闭包一个入参,也可以省略参数列表
myFunc{
$0 > $1
}
  • 逃逸闭包和非逃逸闭包, 当闭包传递进函数的时候,会为闭包进行内存分配。逃逸闭包,是指在函数内的闭包在函数执行结束之后,在函数外还是可以调用。非逃逸闭包,是指在函数生命周期结束后,闭包也会被销毁。默认情况下,函数参数中的闭包都为非逃逸闭包,提高性能,节省内存,同时非逃逸闭包也不能作为返回值,编译时就会出错。
//逃逸闭包声明
func myFunc(closure:@escaping()->Bool)
  • 自动闭包, 实现相对简单的闭包,可以通过自动闭包实现
func myFunc(closure:@autoclosure()->Bool){
}
//调用直接传一个表达式就可以了
myFunc(a>b)

swift 高级运算符和枚举

  • 符号重载
class Circle{
    var center:(Double,Double)
    var radius:Double
    init(center:(Double,Double), radius:Double){
        self.center = center
        self.radius = radius
    }
}
    func +(param1:Circle,param2:Circle)->Circle{
        return Circle(center:param1.center, radius: param1.radius + param2.radius)
    }

在某些情况下函数参数是闭包,比如:

func myFunc(closure:(Circle,Circle)->Circle){
}

//可以直接传入上述代码中定义个加法
myFunc(closure:+)
  • 自定义运算符:在 swift 2.2 之后的版本就移出了 ++ -- 的操作, prefix/infix/postfix 分别代表前中后操作符的声明,其中 prefix/postfix 都只能接收一个参数,func 实现的时候,需要在前面加上 prefix/postfix. infix 接收两个参数,因此不需要在 func 实现的时候加上 infix

prefix operator ++
prefix func ++(param:Int)->Int{
return param+1
}
  • 自定义操作符的优先级
precedencegroup customGroup{
higherThan:AdditionPrecedence//优先级比加法高
lowerThan:MultiplicationPrecedence//优先级比乘法低
assignment:true//设置执行可选链操作时的优先级
associativity:left//结合性
}

infix operator +++:customGroup
infix operator ++++:AdditionPrecedence
  • 枚举
enum SuperName{
    case zhang
    casecase wang
    case 王
}

var sur:SuperName = SuperName.zhang
var sur2:SuperName = .wang
  • 枚举的原始值: 在创建一个枚举的时候,可以声明一个原始值类型,并将某个已经存在的类型的值和枚举值进行绑定
enum CharEnum:Character{
//通过赋值的方式为枚举值设置一个原始值
    case a = "a"
    case b = "b"
    case c = "c"
}
//如果指定原始值类型为 Int 类型,那么可以只设置第一个枚举值的原始值,后面的枚举值会在前一个枚举值的原始值上递增

enum IntEnum:Int{
    case yi = 1
    case er
    case si = 4
    case wu
}

let intEnum1:IntEnum = .wu
print(intEnum1.rawValue)
//通过原始值构造枚举变量
var intEnum2 = IntEnum(rawValue:1)

swift 类与结构体

  • 类是引用类型比较引用类型只能用等同运算符"===", 是对地址的比较,结构体是值类型
class Car
{
    var brand:String
    var price:Double
    init(brand:String, price:Double)
    {
        self.brand = brand
        self.price = price
    }
}
var car1:Car = Car(brand: "BMW", price: 310000)
var car2 = car1
//let isSameObj = car1==car2 //编译错误
//print(isSameObj)
var car3 = Car(brand: "BMW", price: 310000)
let isSame = car1===car3
print(isSame)//false, 是对地址的比较
struct Person{
    var name:String
    var age:Int
    mutating func growUp()//结构体更改结构体的属性,需要加 mutating
    {
        age += 1
    }
}

存储属性&计算属性

  • 存储属性:用于定义类或结构体的某些特征,用变量或者常量存储某些有意义的值。只能用于类和结构体

类的原则: 当类被构造完成时,必须保证类中所有属性都构或者初始化完成,因此类中提供一个构造方法用于设置其中的属性,但也可以在属性声明的时候就提供一个初始值

class Student{
    var name:String
    var age:Int
    let schoolName = "一中" //声明时初始化
    init(name:String, age:Int){
        self.name = name
        self.age = age
    }
}

//如果schoolName不初始化,init 上述的写法就会报错
class Student{
    var name:String
    var age:Int
    let schoolName:String
    init(name:String, age:Int){
        self.name = name
        self.age = age
        self.schoolName = "一直"
    }
}

var st = Student(name: "sss`", age: 15)
//不一定需要构造函数传所有的参数,只需要保证在构造函数体内,所有存储属性都初始化
  • 注意:当值类型的实例是 let 修饰的,那么存储属性是不能修改的。对于引用类型,无论是 let 还是 var,存储属性不是由 let 修饰的,都能够被修改

  • 对于存储属性,还支持将存储属性设置为延时存储属性。延时存储属性是指在类实例构造的时候,延时存储属性并不进行构造或者初始化,只有当调用类实例这个属性的时候,这个属性才完成构造或初始化操作。示例如下

class Persons {
    var age:Int
    lazy var st:Student = Student(name: "mmm", age: 18)
    init(age:Int)
    {
        self.age = age
    }
}
  • 计算属性:计算属性更像一中运算过程,比如一个圆,只需要知道圆心和半径,就可以得到周长和面积,所以就不需要额外定义存储属性去存储周长和面积。在通过 set 方法设置存储属性的时候,新值会以 newValue 的名字传入,当然也可以自定义set(myValue){}的方式来存储这个传入的值. 注意: 计算属性的 get, set 方法,get 方法是必须的,set 方法是可选的。
class Circle1{
    var r:Double
    var center:(Double,Double)
    var l:Double{
        get {
            return 2*r*Double.pi
        }
        set{
            r = newValue/2/Double.pi
        }
}

var s:Double{
        get{
            return r*r*Double.pi
            }
set(myValue)
    {
        r = sqrt(myValue/Double.pi)
    }
}

    init(r:Double, center:(Double,Double))
    {
        self.r = r
        self.center = center
    }
}
var circle1 = Circle1(r: 2, center: (2, 2))

属性监听器

属性监听器:用于监听存储属性的赋值的过程,并且可以自己编写代码,添加额外的逻辑。在进行属性的构造或初始化时,无论是通过构造方法进行属性构造或初始化,还是为属性设置默认值,都不会调用属性监听的方法。初始化后从第2次为属性赋值开始,属性监听器才会被调用

class Teacher{
    var name:String{
    willSet{
        print("will set teacher name:\(newValue)")
    }
    didSet{
        print("did set teacher name:\(oldValue)")
    }
}
    init(name:String)
    {
        self.name = name
    }
}
  • 注意:默认用 oldValue来保存老值, newValue 来保存新值。当然也可以自定义willSet(myNew){}