笔记基于小码哥Swift5.1课程,不涉及深入知识。
结构体
Struct Date {
var year:Int = 2019
var month:Int
var day:Int
}
var date = Date(tear:2019, month:6, day:23)//✅
var date = Date(month:6, day:23)//✅
在Swift标准库中,绝大多数公开类型都是结构体,枚举和类只占很小一部分,所以结构体很常用哦
每个结构体都有一个自动生成的初始化器,这个初始化器会保证所有成员都有初始值,当未自定义初始化器时,如果有未定义的参数,编译器会报错。
下面的代码可以通过编译,因为可选项有默认值
nil
struct Point { var x:Int? var y:Int? } var p = 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)//❌
类
类的定义和结构体相似,但编译器并没有为类自动生成可以传入成员值得初始化器(但也自动生成了一个无参初始化器)。
class Point {
var x:Int = 0
var y:Int = 0
}
let p1 = Point()//✅
let p2 = Point()//❌
在对汇编代码的分析中我们发现,赋值仍然通过初始化器完成(结构体也一样)
类和结构体的本质区别
结构体是 值类型,类是 引用类型 。
类会申请堆空间。
闭包表达式
在Swift里,可以通过func
定义一个函数,也可以通过闭包表达式定义一个函数
func sum(_ v1:Int, _ v2:Int)->Int {
v1 + v2
}
var fn = {
(v1:Int, v2:Int)->Int in
return v1 + v2
}
fn(10,20)
闭包不用写参数名
闭包可以写的很简洁
尾随闭包
如果最有一个实参为闭包且很长时,可以使用尾随闭包。尾随闭包是一个被书写在函数调用括号外的闭包表达式。
exec(v1:Int, v2:int){
$0 + $1
}
闭包
一个函数和他捕获的变量/常量环境结合起来,称为闭包。一般定义在函数内部的函数;一般他捕获的是外部函数的局部变量/常量
func getFn() -> (Int) -> Int {
var num = 0 //num被放到了堆空间。
func Fn(_ i:Int) -> Int {
num += i
return num
}
}//返回的plus和num形成了闭包
var fn = getFn()
print(fn(1))
print(fn(2))
print(fn(3))
print(fn(4))
//1
//3
//6
//10
可以把闭包当成一个类的实例对象,内存在堆空间,捕获的局部变量/常量就是对象的成员,组成闭包的函数就是类内部定义的方法
属性
swift中跟实例相关的属性可以分为两大类:存储属性,计算属性。
struct Cirlce {
//存储属性
var radius:Double
//计算属性
var dimeter:Double {//定义计算属性只能使用var
set {//set传入的新值叫做newValue,只读属性没有get只有set
radius = newValue / 2
}
get {
radius * 2
}
}
}
存储属性类似于成员变量这个概念,存储在实例的内存中。枚举不可以定义存储属性。(实际上是存储实例属性,如果是存储类型属性就ok)
计算属性本质就是方法(函数),不占用实例的内存。枚举,结构体,类都可以定义计算属性。(枚举原始值rawValue的本质是只读计算属性)
//重写rawValue enum Testenum:Int { case tes1 = 1, test 2 = 2, test3 = 3 var rawValue:Int { swift self { case .test1: return 10 case .test2: return 11 case .test3: return 12 } } } print(Testenum.test3.rawValue)//12
延迟存储属性
使用lazy
可以定义一个延迟存储属性,在第一次使用到属性的时候才会进行初始化。
lazy
属性必须是var(let必须在实例的初始化方法完成之前拥有值)
属性观察器
可以为非lazy的var存储属性设置属性观察器。计算属性直接在set{}
中写监听就好了。
struct Circle {
var radius:Double {
willSet {print("willSet",newValue)}
didSet{print("didSet",oldValue,radius)}
}
init(){
self.radius = 1.0
print("Circle init!")
}
}
var circle = Circle()
circle.radius = 10.5
//Circle init!
//willSet 10.5
//didSet 1.0 10.5
在初始化中设置属性值不会触发willSet和didSet。
属性观察器也可以使用在全局,局部变量上。
属性类型
严格来说,属性分为 实例属性, 类型属性。
实例属性:只能通过实例去访问:存储实例属性,计算实例属性。
类型实例:只能通过类型去访问:存储类型属性,计算类型属性。整个程序运行过程中,就只有一份内存。
inout的本质
如果实参有物理地址,且没有设置属性观察器,则直接传入实参的内存地址
如果实参是计算属性或者设置了了属性观察器,则采取Copy In Copy Out的做法(先复制实参的值,产生副本,然后将副本的内存地址传入函数,在函数内修改副本的值。当函数返回后,再讲副本的值覆盖实参的值)
方法
class Car {
static var cout = 0
init() {
car.cout += 1
}
static func getCount() -> Int {
cout
}
}
let c0 = Car()
let c1 = Car()
print(Car.getCount())//3
self
:在实例方法中表示实例,在类型方法代表类型。
mutating
结构体和枚举是值类型,默认情况下值类型不能被自身的实例方法修改。如果要被实例方法修改,使用mutating
修饰方法。
struct Point {
var x = 0.0, y = 0.0
@discardableResult mutating func moveBy(deltaX:Double, deltaY:Double) -> Int {
x += deltaX
y = deltaY
}
}
类是可以修改实例的值的哦
@discardableResult
:不提醒返回值未使用
下标(下标脚本)
使用subscript
可以给任意类型(枚举,结构体,类)增加下标内容,愈发类似于实例方法,计算属性,本质上是方法(函数)。可以使用set,get。
作用:增加使用中括号访问的功能。
继承
值类型(枚举,结构体)不支持继承,只有类支持继承
没有父类色类叫做基类
子类可以重写父类的下标,方法,属性(计算属性),重写必须加上
override
关键字。被static修饰的下标,方法,属性(计算属性)不能被子类重写。(要重写使用class修饰)
子类可以将父类的属性(计算,存储)重写为计算属性。但不可以将计算属性重写为存储属性。
class Animal {
func speak() {
print("Animal speak")
}
}
class Cat:Animal {
override func speak() {
super.Cat//访问父类中的被重写函数
print("Car speak")
}
}
anim = Cat()
anim.speak()//Cat speak
final
被final
修饰的类/属性、方法不能被继承/重写。