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时,强行解包他们会报错,所以需要进行安全判断。
- Swift 允许变量只为空 (nullable), 即
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 类同时遵循Encodable和Decoable两个协议
泛型
泛型是为所存储或使用的一个或多个类型具有占位符(类型形参)的类、结构、接口和方法。
// 泛型方法
//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)"}