swift Part 2

0 阅读14分钟

16 内存布局

区域存储内容管理方式生命周期一句话总结
局部变量、函数参数自动函数调用期间快、小、自动
类实例、闭包捕获变量ARC无强引用时销毁慢、大、ARC管理
全局区全局变量、静态变量自动程序启动到结束全程存活
常量区字符串字面量、只读数据自动程序启动到结束全程存活
代码段函数指令、方法代码自动程序启动到结束全程存活

17 属性

口 存储属性(Stored Property )

  • 存储在实例的内存中, 需要设置初始值
  • 结构体、类可以定义存储属性, 枚举没有

口计算属性(Computed Property)

  • 本质就是方法(函数)
  • 不占用实例的内存, 所以不能设置初始值
  • 枚举、结构体、类都可以定义计算属性

17.1 存储属性

存储属性的核心价值是存储数据, 关于存储属性,Swift有个明确的规定

口 在创建类或结构体的实例时,必须为所有的存储属性设置一个合适的初始值

口 可以在初始化器里为存储属性设置一个初始值

口 可以分配一个默认的属性值作为属性定义的一部

enum Season : Int {
    case spring = 2, summer = 5 // 这不是存储属性
}
var s = Season.spring

// 枚举没有存储属性, 结构设计如此, 枚举占1个字节,区分case及关联值

17.2 计算属性

计算属性的核心价值是动态计算值 口 大括号内 只能写 getter setter代码段 口 声明计算属性 只能用var 不能用let


// 存储属性
var radius: Int = 0
    
// 计算属性
var diameter: Int {
    set {
        radius = newValue / 2
    }

    get {
        radius * 2
    }
}

// 计算属性 省略 get{}的
var getDiameter: Int {
    return diameter
}
// 枚举的rawValue本质上属于计算属性

enum TestEnum : Int {
    case test1 = 1, test2 = 2, test3 = 3

    var rawValue: Int {
        switch self {
        case .test1:
            return 10
        case .test2:
            return 11
        case .test3:
            return 12
        }
    }
}
print(TestEnum.test3.rawValue) // 12

17.3 延迟存储属性

class Car {
    init() {
        print("car--init")
    }
    
    func run() {
        print("车开动了")
    }
}

class Man {
   lazy var car: Car =  Car()

    init() {
        print("man--init")
    }
}

let m = Man()

// 原始打印
// man -- init
// car -- init
// ---
// 车开动了


// 添加lazy打印
// man -- init
// ---
// car -- init
// 车开动了

17.4 属性观察器 (Property Observer)

可以为非lazy 的 var 存储属性设置属性观察器

// 存储属性才能设置属性观察器 ( 虽然和计算属性长的很像 )

// so 存储属性设置默认值很正常了

struct Circle {
    var radius: Double = 0 {
        willSet {
            print(newValue) 
        }

        didSet {
            print(oldValue,radius) 
        }
    }
}

var cir = Circle()
cir.radius = 10.0

// 10.0
// 0.0 10.0

17.5 inout输入输出函数

struct Shape {
    var width: Int // 存储属性

    var side: Int { // 存储属性
        willSet {
        print("willsetSide", newValue)
        }
        didSet {
        print("didSetSide", oldValue, side)
        }
    }

    var girth: Int { // 计算属性
        set {
           width = newValue / side
           print ("setGirth", newValue)
        }
        get {
            print("getGirth")
            return width * side
        }
    }

    func show() {
        print("width=\(width), side=\(side), girth=\(girth)")
    }
}
// 错误写法

func test(_ number: Int) {
    number = 20 // Cannot assign to value: 'number' is a 'let' constant
}
// 正确写法

func test(_ number: inout Int) {
    number = 20
}

var s = Shape(width: 10, side: 4) // 初始化不调用属性观察器

test (&s.width)
s.show()
print("---")

//      getGirth   -- ps: s.show()打印的
//      width=20, side=4, girth=80
//      ---

test(&s.side)
s.show()
print("---")

//      willsetSide 20
//      didSetSide 4 20
//      getGirth
//      width=20, side=20, girth=400
//      ---

test (&s.girth)
s.show()

//      getGirth   -- ps: test (&s.girth)
//      setGirth 20  -- ps: ps: test (&s.girth)
//      getGirth  -- ps: s.show()
//      width=1, side=20, -- girth=20 ps: s.show()

17.6 类型属性 static class

严格来说,属性可以分为: 实例属性, 类型属性

  • 实例属性(Instance Property):只能通过实例去访问, 包含: 存储实例属性, 计算实例属性

  • 类型属性(Type Property):只能通过类型去访问, 包含: 存储类型属性, 计算类型属性

  • 可以通过static定义类型属性, 如果是类,可用Class定义可重写的类型属性

  static var age: Int = 18 // 存储属性
 
  static var name: String { // 注意这是个计算属性
        "jack"
  }
  
  override func viewDidLoad() {
        super.viewDidLoad()
        let age = FTXibViewController.age
        let name =  FTXibViewController.name
  }

不同于存储实例属性,你必须给存储类型属性设定初始值

  • 因为类型没有像实例那样的 init 初始化器来初始化存储属性

存储类型属性默认就是lazy,会在第一次使用的时候才初始化

  • 就算被多个线程同时访问,保证只会初始化一次口
  • 存储类型属性可以是let

枚举类型也可以定义类型属性(存储类型属性、计算类型属性)


class Animal {
    // 使用 class 定义可重写的类型计算属性
    class var species: String {
        return "动物"
    }

    // 使用 static 定义不可重写的类型计算属性
    static var kingdom: String {
        return "动物界"
    }
}

class Dog: Animal {
    // ✅ 可以重写 class 属性
    override class var species: String {
        return "犬科动物"
    }

    // ❌ 不能重写 static 属性
    // override static var kingdom: String { ... }  // 错误!
}
// 枚举的类型属性

enum Shape {
    static var width: Int = 0
    
    var height: Int { // 这个看着没啥用,但是计算属性rowValue还是有用的
        0
    }
    
    case s1, s2, s3, s4
}

var s = Shape.s1 // 通过枚举名, 访问枚举实例

var s1 = Shape.width // 通过枚举名, 访问枚类型属性

var s2 = Shape.s2.height // 通过实例访问普通计算属性

18 单例

public class FileManager { // 公开的类,可被其他模块访问
    public static let shared = FileManager() // 公开的静态常量,持有唯一实例
    private init() {} // 私有构造器,外部无法创建新实例
}
// ✅ 获取单例
let manager = FileManager.shared

// ❌ 无法创建新实例
let another = FileManager()  // 编译错误:init is private

19 方法

枚举、结构体、类都可以定义实例方法、类型方法

  • 实例方法(Instance Method):通过实例对象调用

  • 类型方法(Type Method):通过类型调用,用static或者class关键字定义

class Car {
    static var cout = 0
    
    static func getCount() -> Int {
        cout
    }
    
    init() {
        Car.cout += 1
    }
}

let c0 = Car()
let c1 = Car()
let c2 = Car()

print(Car.getCount()) // 3

20 mutating

作用: 添加mutating关键字, 实现值类型内部实例方法修改自身属性值

mutating: 转换/转变

// 结构体实例方法 修改自身属性

struct Point {
    var x = 0.0
    var y = 0.0

    mutating func moveBy(deltaX: Double, deltaY: Double) {
        x += deltaX
        y += deltaY
        // self = Point(x: × + deltaX, y: y + deltaY)
    }
}

// 直接修改方法传入的参数值

func test(num1: inout Int) {
    num1 = 10
}

21 discardableResult ( 可丢弃的结果 )

  • 作用: 消除函数调用后返回值未使用的警告 (消除警告)
@discardableResult func get() -> Int {
  return 10
}

func get1() -> Int {
  return 10
}

get()
get1() //  ⚠️ Result of call to 'get1()' is unused

22 下标 subscript

作用: 本质是函数, 可以像数组一样调用函数

  • 使用subscript可以给任意类型(枚举、结构体、类)增加下标功能,有些地方也翻译为:下标脚本
  • subscript的语法类似于实例方法、计算属性,本质就是方法(函数)
// 实例下标
// 给类 Point 添加下标, 使其可以类似数组一样 丝滑获取及修改属性x,y

class Point {
    var x = 0.0
    var y = 0.0
    
    subscript(index: Int) -> Double {
        set {
            if index == 0 {
                x = newValue
            } else if index == 1 {
                y = newValue
            }
        }
        get {
            if index == 0{
                return x
            } else if index == 1 {
                return y
            }
            return 0
        }
    }
}

let p = Point()
p[0] = 11.1
p[1] = 22.2

print(p.x) // 11.1
print (p.y) // 22.2
print(p[0]) // 11.1
print(p[1]) // 22.2
// 类型下标

class Sum {
    static subscript(v1: Int, v2: Int) -> Int {
        return v1 + v2
    }
}

print(Sum[10,20]) // 30

23 继承 ( Inheritance )

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

  • 没有父类的类,称为:基类
  • Swift并没有像OC、Java那样的规定:任何类最终都要继承自某个基类

子类可以重写父类的下标、方法、属性,重写必须加上 override 关键字

23.2 重写实例方法及下标 overrride

  • 想实现父类方法, 调用super
  • 不想实现父类方法, 不调用super
  • swift 重写父类方法, 强制要求加 overrride, 这点不同于oc 的直接覆盖
class Animal {
    func speak() {
        print("Animal speak")
    }
    subscript(index: Int) -> Int {
        print("---\(index)")
        return index
    }
}

class Cat: Animal {
   override func speak() {
       super.speak() //不想实现父类方法, 可以不调用super  
        print("Cat speak")
    }
   override subscript(index: Int) -> Int {
       super[index]
       print("---\(index + 1)")
        return index + 1
    }
}
let a = Animal()
a.speak()
a[6]

// Animal speak
// ---6

let c = Cat()
c.speak()
c[6]

// Animal speak
// Cat speak
// ---6
// ---7

24.2 重写类型方法及下标 class

  • 被class修饰的类型方法、下标,允许被子类重写

  • 被static修饰的类型方法、下标,不允许被子类重写

class Animal {
   class func speak() {
        print("Animal speak")
    }
   class subscript(index: Int) -> Int {
        print("---\(index)")
        return index
    }
}

class Cat: Animal {
    class override func speak() {
       super.speak()
        print("Cat speak")
    }
    class override subscript(index: Int) -> Int {
       super[index]
       print("---\(index + 1)")
        return index + 1
    }
}
Animal.speak()
Animal[6]

// Animal speak
// ---6


Cat.speak()
Cat[6]

// Animal speak
// Cat speak
// ---6
// ---7

24.3 重写属性 override

子类可以将父类的属性(存储、计算)重写为计算属性

子类不可以将父类属性重写为存储属性

只能重写var属性,不能重写let属性

重写时,属性名、类型要一致

子类重写后的属性权限 不能小于 父类属性的权限

  • 如果父类属性是只读的,那么子类重写后的属性可以是只读的、也可以是可读写的
  • 如果父类属性是可读写的,那么子类重写后的属性也必须是可读写的
class Circle {
    // 存储属性 添加属性观察器
    var radius: Int = 0 {
        willSet {
            print("--circle--will- \(newValue)")
        }
        didSet {
            print("--circle--did- \(oldValue)--\(radius)")
        }
    }
    var diameter: Int = 0 {
        willSet {
            print("--circle--will- \(newValue)")
        }
        didSet {
            print("--circle--did- \(oldValue)--\(diameter)")
        }
    }
}

class SubCircle: Circle {
   // 计算属性
   override var radius: Int {
        set {
            print("SubCircle setRadius")
            super.radius = newValue > 0 ? newValue : 0
        }
        
        get {
            print("Sub(ircle getRadius")
            return super.radius
        }
    }
    override var diameter: Int {
        set {
            print("'SubCircle setDiameter")
            super.diameter = newValue > 0 ? newValue : 0
        }
        
        get {
            print("SubCircle getDiameter")
            return super.diameter
        }
    }
    
}
var circle = SubCircle()
circle.radius = 6 // SubCircle setRadius, --circle--will- 6,  --circle--did- 0--6
print(circle.diameter) // SubCircle getDiameter , 0

24.4 属性监听 willSet didSet

  • 计算属性不可以和属性观察器一起写, 但是通过子类可以实现对父类的计算属性实现监听
  • 计算属性的特殊情况也可以直接监听了
class Rectangle {
    // 实例属性
    var width: Double = 0 {
        willSet {
            print("width 将要变成: (newValue)")
        }
        didSet {
            print("width 从 (oldValue) 变成了 (width)")
            // 注意:area 是计算属性,改变 width/height 会自动重算
        }
     }
     // 类型属性
    static var height: Double = 0 {
        willSet {
            print("--Rectangle--will- \(newValue)")
        }
        didSet {
            print("--Rectangle--did- \(oldValue)--\(height)")
        }
    }
    //
    ```
    // 计算属性
    var area: Double {
        get {
            print("正在读取 area 的值")  // 获取当前值
            return width * height
        }
        set {
            print("正在设置 area 为: (newValue)")  // 获取新值
            width = sqrt(newValue)
            height = sqrt(newValue)
        }
    }  
}
class SubRectangle: Rectangle {
 // 监听父类的计算属性 area
    override var area: Double {
        willSet {
            print("SubRectangle----\(newValue)")
        }
        
        didSet {
            print("SubRectangle----\(oldValue)")
        }
    }
}

24 final 函数

  • 被final修饰的方法、下标、属性,禁止被重写
  • 被final修饰的类,禁止被继承

ps: final: 终结

// 方法禁止被重写

class Rectangle {
   final func test(){}
}

class SubRectangle: Rectangle {
    override func test(){} // ❌ Instance method overrides a 'final' instance method
}
// 类禁止被继承

final class Rectangle {
   final func test(){}
}

class SubRectangle: Rectangle { // ❌ Inheritance from a final class 'Rectangle'
}

25 初始化器

类、结构体、枚举, 都可以定义初始化器

指定初始化器(designated initializer ): 确保所有属性都被初始化

便捷初始化器(convenience initializer ): 确保所有属性都被初始化 ( 代码层面书写更简单 )

  • 指定初始化器, 和便捷初始化器 都可一设置多个
// 指定初始化器 vs 便捷初始化器

class Person {
    var name: String
    var age: Int
    
    // 指定初始化器:确保所有属性都被赋值
    init(name: String, age: Int) {
        self.name = name  // ✅ name 被初始化
        self.age = age    // ✅ age 被初始化
    }
    
    // 便捷初始化器:只是叫指定初始化器干活
    convenience init(name: String) {
        self.init(name: name, age: 0)  // 调用指定初始化器
    }
}

let p = Person(name: "张三", age: 20)  // 对象完整可用

let p1 = Person(name: "张三")  // 调用简单了些
class VipUser: User {
     var level: Int
     
    // 子类的指定初始化器
    init(name: String, age: Int, level: Int) {
        self.level = level
        super.init(name: name, age: age) // 不写会报错!, 确保子类的所有属性都初始化
    }
}

25.1 初始化器继承

  • 指定初始化器子类也能继承
  • 如果父类自定义了指定初始化器, 子类的默认指定初始化器会被覆盖

class Animal {
    var name: String
    init(name: String) {
        self.name = name
    }
}

// 子类没有定义任何初始化器
class Dog: Animal {

}

let dog = Dog(name: "旺财")

// let dog1 =  dog() // ❌ 默认初始器被覆盖

26 require 强制重写关键字

作用:强制所有子类必须实现该初始化器(要么继承,要么重写)

  • 如果子类重写了 required 初始化器,也必须加上 required , 不用加 overrid
class Animal {
    var name: String
    
    // required 修饰:所有子类都必须有这个初始化器(继承 或 重写)
    required init(name: String) {
        self.name = name
    }
}
// 继承
class Pig: Animal {
 
}
// 重写
class Dog: Animal {
    var breed: String
    
    // 必须实现 required 初始化器
    required init(name: String) {
        self.breed = "Unknown"
        super.init(name: name)
    }
}
// 重写
class Cat: Animal {
    var color: String
    
    // 自己的指定初始化器
    init(name: String, color: String) {
        self.color = color
        super.init(name: name)
    }
    
    // 因为有了自己的指定初始化器,必须重写 required
    required init(name: String) {
        self.color = "White"
        super.init(name: name)
    }
}

27 可失败初始化器 init?

  • 可以创建失败 nil
// 类
class Person {
    var name: String
    init?(name: String) {
        if name.isEmpty {
            self.name = name
        }
        return nil 
    }
}

// ⚠️ !!! 正常初始化器 是不能返回的
// 枚举
enum Temperature {
    case hot, cold
    
    init?(celsius: Double) {
        if celsius > 30 {
            self = .hot
        } else if celsius < 10 {
            self = .cold
        } else {
            return nil
        }
    }
}
// 结构体
struct Size {
    var width: Double
    
    init?(width: Double) {
        if width < 0 {
            return nil
        }
        self.width = width
    }
}

28 反初始化器 deinit

  • 类似 oc 的 dealloc
  • deinit 不接受任何参数,不能写小括号,不能自行调用
  • 父类的 deinit 能被子类继承
  • 子类的 deinit 实现执行完毕后会调用父类的deinit
// 父类
class Animal {
    var age: Int = 0
  
    init(age: Int) {
        self.age = age
    }
    
    deinit {
        print("animal is die")
    }
}
// 子类
class Dog: Animal {
    deinit {
        print("dog is die")
    }
}

Dog(age: 5)
// dog is die
// animal is die

29 协议

作用: 继承了协议, 就要遵守协议的规则(方法, 属性, 下标等)

  • 协议中定义方法时不能有默认参数值
  • 默认情况下,协议中定义的内容必须全部都实现
  • 定义属性必须var

29.1 遵守多个协议

// 声明协议
protocol Test1 {}
protocol Test2 {}
protocol Test3 {}

// 同时遵守多个协议
class TestClass : Test1, Test2, Test3  {}
// 声明协议
protocol Drawable {
    func draw()
    var x: Int { get set } // 读/写 属性
    var y: Int { get } // 只读属性
    subscript(index: Int) -> Int { get set}
}

29.2 遵守 & 实现协议

属性 方法 类型方法 下标

// 遵守协议
class Woman : Drawable {
    var x: Int = 0 // var 实现读写属性
    let y: Int = 0 // let 实现仅读属性
    
    func draw() {
        print ("Woman draw")
    }
    
    static func run() { // static不可重写
        print ("Woman run")
    }
    
    subscript(index: Int) -> Int {
        set {}
        get { index }
    }
}
// 遵守协议
class Man : Drawable  {
    var x: Int { // 计算属性实现
        set {
            print(newValue)
        }
        
        get {
           0
        }
    }
    
    var y: Int { // 计算属性实现
        get {
            return 0
        }
    }
    
    func draw() {
        print ("Man draw")
    }
    
    class func run() { // class 可以重写
        print ("Man run")
    }
    
    subscript(index: Int) -> Int {
        set {}
        get { index }
    }
}

29.3 协议中 mutating

// mutating作用: 结构体, 枚举自身实例方法,能修改自身存储属性
protocol Drawable {
    mutating func draw() // 如果值类型遵守协议,k可以在这方法中修改自身存储属性
}

// 我是类 可以不加
class Size : Drawable {
    var width: Int = 0
    func draw() {
        width = 10
    }
}

// 我是值类型 加上了, 可以修改自身属性值
struct Point1 :Drawable {
    var x: Int = 0
    mutating func draw() {
        x = 10
    }
}

29.4 协议中 init init? init!

协议中定义的init?、init!,可以用init、init?、init!去实现

protocol Livable {
    init ()
    init?(age: Int)
    init!(no: Int)
}

class Person : Livable {
    required init() {}
    required init?(age: Int) {}
    required init! (no: Int) {}
}

29.5 协议继承

  • 协议可以继承
protocol Runnable {
    func run()
}

protocol Livable: Runnable {
    func breath()
}

class Person: Livable {
    func breath(){}
    func run(){}
}

29.6 协议组合

protocol Livable {}
protocol Runnable {}
class Person {}

// 接收Person或者其子类的实例
func fn0(obj: Person) {}

// 接收遵守Livable协议的实例
func fn1(obj: Livable) {}

// 接收同时遵守Livable、Runnable协议的实例
func fn2(obj: Livable & Runnable) {}

// 接收同时遵守Livable、Runnable协议、并且是Person或者其子类的实例
func fn3(obj: Person & Livable & Runnable) {}

// 接收同时遵守Livable、Runnable协议、并且是Person或者其子类的实例
typealias RealPerson = Person & Livable & Runnable

func fn4 (obj: RealPerson){}

29.6 遵守协议日常

enum Season : CaseIterable {
    case spring, summer, autumn, winter
}
// 枚举遵守了协议可以使用 Season.allCases 类方法
let seasons = Season.allCases

print(seasons.count) // 4

for season in seasons {
    print (season)
}

// spring summer autumn winter

// 打印协议 CustomstringConvertible、 CustomDebugStringConvertible 协议

30 类型 Any AnyObject

Any: 存放任何类型

AnyObject: class的实例, (Objective‑C 里 id 对应 AnyObject)

// Any: 存放任何类型

var a: Any = 10
a = "hi"
a = [1,2,3]
a = { print(1) }
a = test()
a = TestViewController.test1()
a = {(v1:Int, v2:Int) in v1 + v2}
// AnyObject: 类的实例

struct mModel {
    var name: String
    var age: Int
}

var b: AnyObject = mModel(name: "小华", age: 10) // ❌ 报错
var c: AnyObject = Person()      
// 定义协议 (仅仅能被类遵守)
protocol XXProtocol: AnyObject {
    var num: Int{get set}
}

struct model: XXProtocol { // ❌ Non-class type 'model' cannot conform to class protocol 'XXProtocol' 非类类型的model 不能遵守协议
    var num: Int = 0
    var name: String
    var age: Int
}
 
class TestViewController: UIViewController, XXProtocol {
    var num: Int = 0

    override func viewDidLoad() {
        super.viewDidLoad() 
    }
}