【Swift5.x】面向对象掠影

笔记基于小码哥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的做法(先复制实参的值,产生副本,然后将副本的内存地址传入函数,在函数内修改副本的值。当函数返回后,再讲副本的值覆盖实参的值)


方法


枚举,结构体,类都可以定义12

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。

作用:增加使用中括号访问的功能。

【Swift5.x】面向对象掠影

【Swift5.x】面向对象掠影

继承

值类型(枚举,结构体)不支持继承,只有类支持继承

没有父类色类叫做基类

子类可以重写父类的下标,方法,属性(计算属性),重写必须加上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修饰的类/属性、方法不能被继承/重写。


  1. 通过实例调用 ↩︎

  2. 通过类型调用,用static,class关键字定义 ↩︎

上一篇:swift5表情键盘项目封装


下一篇:swift5展示全球国家列表