Swift 入门与实践 | 青训营笔记

127 阅读6分钟

Swift 入门与实践

这是我参与「第四届青训营 -IOS场」笔记创作活动的第4篇笔记

基础语法

  • 变量定义
//可变变量 var
var name : String = "Tom"
//不变变量 let
let price : Int = 300 //不可对 price 进行再次赋值
  • Swift 可以自动判断类型,比如在 name, price 赋值时,即使没有声明它们的变量类型,swift 也会根据所赋的值来推断是 String 和 Int.

  • 数组,字典,合集

//数组 array - 有序
var name : [String] = ["Tom", "James", "Jack"]
//字典 dict - 无序
var scores : [String : [String : Int]] = ["Tom": ["Math": 95, "English": 88], "James": ["Math": 80, "English": 95]]
//合集 set - 无序, 不重复
var classes : Set<String> = ["Math", "English"]
  • 可选值 Optional
    • Swift 允许变量只为空 (nullable), 即 nil: var score : Int? = nil
    • 但是在使用时有时候需要解包(unwarp)他们,即把它们转换成非空变量 (nonnull)。 可如果变量值为nil时,强行解包他们会报错,所以需要进行安全判断。
let optional : Int?
var value : Int

//Force Unwrapping - if we really sure the optional will never be nil.
value = optional!

//Check for nil value - wordy, still need unwrapping
if optional != nil {
    value = optional!
}

//Optional Binding 
if let safeOptional = optional {
    value = safeOptional //unwrapped 
}

//Nil Coalescing Operator
let defaultValue : Int = 0
value = optional ?? defaultValue

//Optional Chaining - for optional struct/class
optional?.property
optional?.method()

函数

函数声明与调用

func greet(name person: String) -> String {
    let greeting = "Hello, " + person + "!"
    return greeting
}

greet(name: "Peter")
  • 这个函数接受一个 String 变量, 返回一个String 变量
  • name 是外参, person 是内参
    • 即在使用时我们会用外参指名变量 greet(name: "Peter")
    • 而在内部调用时,我们会用内参来引用变量
    • 外参可省略 func greet(person: String) -> String,在这情况下,内外参一样都为person
    • 外参可替换成_func greet(_ person: String) -> String,在这情况下,外部调用不需要指名参数名: greet("Peter")

Inout

  • Swift函数调用时,参数传递默认都是复制传递,即内部对参数的处理不会影响到外部原来的参数。
  • 如果需要引用传递,即在参数变量类型前加 inout
func swapTwoDoubles(_ a: inout Double, _ b: inout Double) {
    let temporaryA = a
    a = b
    b = temporaryA
}

var a = 8.0
var b = 7.0
swapTowDoubles(&a, &b) // 加上&表示引用
print(a) // 7.0
print(b) // 8.0

类和结构体

类 Class

//声明类 User
class User{
    //类属性
    let name : String
    var deposit
    
    //初始化器
    init (name: String, deposit: Float = 0){
        self.name = name
        self.deposit = deposit
    }
    
    //类函数
    func addMoney(money: Float){ Deposit += money }
    func introduction(){ print("hi, I am a user \(self.name).") }
    //静态函数
    //注意:静态函数不会被子类继承
    static myType{print("I am a User type")}
}

//声明类 Player, 继承于User 类
class Player : User {
    var level :
    init(name: String, deposit: Float = 0.0 , level : Int ){
        self.level = level
        super.init(name: name, deposit: deposit) 
    }
    //父类函数复写
    override func introduction(){ print("hi, I am a player \(self.name).") }
}

//类的实例化与属性,方法函数
var peter = Player(name: "Peter", deposit: 200.0, level : 2)
peter.addMoney(50.0)
print(peter.deposit) //250.0
//静态函数访问
User.myType() //"I am a User type"
Player.myType() //报错 Player 没有静态函数 "myType"

  • 类的扩展
    • 扩展不止可用于扩展类,也可以用于结构体,枚举类型和协议
    • 扩展时也可以为类,结构体添加协议
extension Player{
    func levelUp(){self.level += 1}
}
peter.levelUp()
print(peter.level) //3

结构体 Struct

struct User {
    //结构体属性
    let name : String
    var deposit : Float = 0.0
    //结构体函数
    mutating func addMoney(m money: Float){
        deposit += money
    }
}
//结构体的实例化与属性,函数访问与类相同
  • 结构体的属性对于他内部的函数来说是不可变的(immutable)
    • 如果要在结构体内部的函数里改变自身的属性,需要用 var 来声明属性,同时在函数前加 mutating 关键词
    • 它的实际操作是 销毁原有结构体实例,然后使用创建新实例

类和结构体的区别

  • 结构体是值类型, 类是引用类型
  • 类实例是在赋值和参数传递时都是引用传递(copied/passed by reference),而结构体是值传递(copied/passed by value)。
  • 结构体自带初始化器,而类没有,需要主动声明初始化器
  • 类是可变类型(mutable),结构体是不可变类型(immutable)
  • 类和结构体都可以遵循协议
  • 类可以被继承,而结构体不行。

  • 什么时候用类?什么时候用结构题?
    • 默认状态下尽量使用结构体
    • 如果需要与OC 代码交互,用类
    • 当你需要控制数据的特性时,用类

协议 Protocol

协议的声明与遵循

//协议的声明 
protocol AdvancedLifeSupport{
    //协议属性, 必须用 get set 来声明其读写属性, get-可读, set-可写
    var handler: EmergencyCallHandler { get set } 
    //协议方法
    func performCPR()
}

class EmergencyCallHandler {
    var delegate: AdvancedLifeSupport? 
    func assessSituation(){print("What was the emergency?")}
    func medicalEmergency(){delegate?.performCPR()}
}

//协议遵循
struct Paramedic: AdvancedLifeSupport {
    var handler: EmergencyCallHandler
    init(handler: EmergencyCallHandler){
        self.handler = handler
        self.handler.delegate = self
    }
    func performCPR(){
        print("The paramedic does chest compressions, 30 per second.")
    }
}

//协议遵循
class Doctor : AdvancedLifeSupport {
    var handler: EmergencyCallHandler
    init(handler: EmergencyCallHandler){
        self.handler = handler
        self.handler.delegate = self
    }
    func performCPR(){
        print("The paramedic does chest compressions, 30 per second.")
        print("Listening for heart sounds")
    }
}
//协议遵循可被继承
class Surgeon: Doctor {
    override func performCPR(){
        super.performCPR()
        print("using Electric Drill")
    }
}

//usage
let tom = EmergencyCallHandler()
let pete = Paramedic(handler: tom)
tom.assessSituation()
tom.medicalEmergency()

协议的使用

协议正如数据类型,可以:

  • 在函数、方法或者初始化器里作为形式参数类型或者返回类型;
  • 作为常量、变量或者属性的类型;
  • 作为数组、字典或者其他存储器的元素的类型。

协议的其他变型

  • @mutating:表示该方法可以改变所属的实例
  • @required: 该修饰符用于初始化器,表明遵循该协议的类必须拥有指定格式的初始化器, 比如带有指定参数
  • @required & @override:如果在满足@required的前提下还复写了父类的初始化器那么就得加上@override,但如果子类带final那就不用再用@required修饰。
  • 协议的多个使用:直接用逗号分隔或&, 如 class User:Encodable, Decoable{}表明User 类同时遵循 EncodableDecoable 两个协议

泛型

泛型是为所存储或使用的一个或多个类型具有占位符(类型形参)的类、结构、接口和方法。

// 泛型方法
//T 为类型形参占位符,可作为形参或者返回值类型
func swapTwoValues<T>(_ a: inout T, _ b: inout T) { 
    let temporaryA = a 
    a = b 
    b = temporaryA 
}
//使用
var a : String = "hello"
var b : String = "world"
swapTwoValues(&a, &b)

//泛型类型 -将占位符写在类型名后
class RandomSelector<Element> {
    func randomSelect([Element]) -> Element {..}
}
let rs = randomSelector<Int>()
rs.randomSelect([1,2,3])

闭包

//operation 函数作为传入参数
func calculator (n1: Int, n2: Int, operation: (Int, Int) -> Int) -> Int{
    return operation(n1,n2)
}

//V1: 没有使用闭包,函数作为传入参数使用
func add (n1: Int, n2: Int) ->Int{ return n1 + n2}
calculator(n1: 1, n2: 2, operation: add)

//V2: 闭包格式
calculator(n1:1, n2: 2, operation: {(n1 Int, n2 Int) -> Int in 
    return n1 + n2
})

//V2.1: 让swift自行判断参数类型
calculator(n1:1, n2: 2, operation: {(n1, n2) in 
	n1 + n2 //因为只有一个表达式, 单表达式闭包,return关键字也可以被省略 
	})  

//V3: 简写实际参数名 
calculator(n1:1, n2: 2, operation: {$0 * $1}) 
//$0, $1, $2.. 分别代表第一第二第三..个参数


//V4:尾随闭包 - 即闭包为函数的最后一个参数
calculator(n1:1, n2: 2) {$0 * $1}  

//闭包用法
let array = [6,2,3,9,4,1]
let addOne = array.map{$0 + 1} //array.map({(n) in n + 1})
let stringify = array.map{"\($0)"}