Swift-26-面向对象OOP编程-类和属性定义

从本章开始,我们会抛弃playgroup工程,转而采用Command Line Tool工程来实现专题中的例子。

类对象

用来抽象成一个通用类型 相关数据建模。

类的语法,使用关键字class来定义:class className { }

类的创建

一个简单的自定义类的例子

swift 复制代码
// Monster.swift

import Foundation

class Monster {
    var town: Town?
    var name = "Monster"
    
    func terrorizeTown() {
        if town != nil { //注意这种非空的判断方法
            print("\(name) is terrorizing a town!")
        } else {
            print("\(name) hasn't found a town to terrorize yet...")
        }
    }
}

类Monster的town属性为结构体,在swift中结构体和类差不太多。

swift 复制代码
struct Town {
    var population = 5422
    var numberOfStoplights = 4
    
    func printDescription() {
        print("Population: \(population); number of stop lights: \(numberOfStoplights)")
    }
    
    mutating func changePopulation(by amount: Int) {
        population += amount
    }
}

类的实例化使用

swift 复制代码
// Main.swift
var myTown = Town();
let genericMonster = Monster()
genericMonster.town = myTown
genericMonster.terrorizeTown() //Monster is terrorizing a town!

类的继承

语法和objective-c一样,全是用:号,下面的 Zombie 类继承了 Monster 类。在这种继承中可以用self和super关键字。

swift 复制代码
Zombie.swift
class Zombie: Monster {
    var walksWithLimp = true //增加了一个新属性
    
    //重写了父类的terrorizeTown方法,final表示再有子类时不允许再重写了
    final override func terrorizeTown() {
        town?.changePopulation(by: -10) //空链式调用,省去了if判断
        super.terrorizeTown()
    }
}

测试代码

swift 复制代码
//Main.swift
let fredTheZombie = Zombie()
fredTheZombie.town = myTown
fredTheZombie.terrorizeTown() //Monster is terrorizing a town!
fredTheZombie.town?.printDescription() //Population: 5912; number of stop lights: 4

静态方法

在结构体中定义静态方法需要用到static关键字,在类中可以使用class和static两种关键字来标记,区别在于:

  • 用class关键字时,此时子类可以像普通方法一样复写父类的静态方法;
  • 用static关键字,此时子类不能复写了,有点类似final的作用;
swift 复制代码
class Monster {
    class func makeSpookyNoise() -> String { //String表示参数
        return "Brains..."
    }
    
    static func makeSpookyNoise() -> String {
        return "Brains..."
    }
} 

let msn = Monster.makeSpookyNoise()

属性

类、结构体和枚举都可以有属性,属性分为存储和计算属性两种,前者可以有默认值,后者则会根据现有信息返回某种计算结果。可以监听属性的变化,也可以给属性设定一些规则。

属性的存储

就是通常所用let和var定义的变量,示例代码如下:

  • 普通的存储属性,只用于属性的简单存取
swift 复制代码
//-----
class Monster {
    var town: Town?
    var name = "Monster"
}
  • 嵌套属性,即一个属性内部还有其它属性,比如枚举
swift 复制代码
//------
struct Town {
    var population = 5_422 //这种表示方法同5422是一个意思
    var numberOfStoplights = 4
    
    enum Size {
               case small
               case medium
               case large
    }
}    

lazy惰性存储,使用才初始化

主要是为了节省内存,只在第一次被访问的时候才会真正初始化,使用关键字lazy var 。它可以引用闭包或函数,以实现依赖其它值来初始化属性的目的。

语法格式: lazy var propertiesName: Type = {}()

swift 复制代码
struct Town1 {
    static let region = "South"
    
   enum Size {
        case small
        case medium
        case large
    }
    var population = 5_422
    
    //声明lazy属性townSize,类型为Size。然后定义一个闭包函数来计算此属性的真实值
    lazy var townSize: Size = {
         switch self.population {
             case 0...10_000:
                 return Size.small
             case 10_001...100_000:
                 return Size.medium
             default:
                 return Size.large
         }
    }()   //这里的()类似一个方法调用的方式,比如func()。
}

//测试,这里需要注意即使更改了population的值,也调用了一次lazy属性,然而再手动更改population值时也不会自动更新townSize的值,因为它们并不是连动的。
var myTown1 = Town1()
let myTown1Size = myTown1.townSize
print(myTown1Size) //small

getter/setter属性计算

它不存储值,而是提供一个getter方法来获取属性的值,也可提供一个setter方法来设置属性的值。这种方式正好弥补了lazy属性不自动更新的弊端。

语法格式: var propertiesName: Type { get{} set(){} }

swift 复制代码
struct Town {
    static let region = "South"
    var population = 5_422 { //这种用法叫观察都后面会讲
        didSet(oldPopulation) {
            print("The population has changed to \(population) from \(oldPopulation).")
        }
    }
    var numberOfStoplights = 4
    
    enum Size {
        case small
        case medium
        case large
    }
    
    var townSize: Size {
        get {
            switch self.population {
            case 0...10_000:
                return Size.small
                
            case 10_001...100_000:
                return Size.medium
                
            default:
                return Size.large
            }
        }
    }
    
    func printDescription() {
        print("Population: \(population); number of stop lights: \(numberOfStoplights)")
    }
    
    mutating func changePopulation(by amount: Int) {
        population += amount
    }
}

//测试
var myTown = Town()
let myTownSize = myTown.townSize
print(myTownSize) //smal

//值会重新计算,相当于连动了
myTown.changePopulation(by: 1_000_000) //The population has changed to 1005422 from 5422.
print("Size: \(myTown.townSize); population: \(myTown.population)") //Size: large; population: 1005422

读取和写入方法

即同时声明getter和setter方法,实现属性的读取方法。

swift 复制代码
class Zombie: Monster {
    override class var spookyNoise: String {
        return "Brains..."
    }
    
    var walksWithLimp = false
    private(set) var isFallingApart = false
    
    final override func terrorizeTown() {
        if !isFallingApart {
            town?.changePopulation(by: -10)
        }
    }
}
swift 复制代码
class Monster {
    class var spookyNoise: String {
        return "Grrr"
    }
    static let isTerrifying = true
    var town: Town?
    var name = "Monster"
    
    //这里~~
    var victimPool: Int {
        get {
            return town?.population ?? 0
        }
        set(newVictimPool) {
            town?.population = newVictimPool
        }
    }
    
    func terrorizeTown() {
        if let _ = town {
            print("\(name) is terrorizing a town!")
        } else {
            print("\(name) hasn't found a town to terrorize yet...")
        }
    }
}

测试代码

swift 复制代码
var myTown = Town()
let fredTheZombie = Zombie()
fredTheZombie.town = myTown
fredTheZombie.terrorizeTown() //The population has changed to 5412 from 5422.
fredTheZombie.town?.printDescription() //Population: 5412; number of stop lights: 4

print("Victim pool: \(fredTheZombie.victimPool)") //Victim pool: 5412
//设置值
fredTheZombie.victimPool = 500 //The population has changed to 500 from 5412.
print(Zombie.spookyNoise) //Brains...
print("Victim pool: \(fredTheZombie.victimPool)") //Victim pool: 500

/*
The population has changed to 5412 from 5422.
Population: 5412; number of stop lights: 4
Victim pool: 5412
The population has changed to 500 from 5412.
Brains...
Victim pool: 500
*/

属性变化观察者

属性观察者会观察并响应给定属性的变化,它可以用于任何自定义的存储属性和任何继承的属性,但不能用于计算属性。

语法格式: var propertiesName: Type = value { diSet(oldV){} willSet(newV){} }

上述代码中在调用:fredTheZombie.terrorizeTown() 会改变population属性的值,此时就可以用didSet来观察值,切入一次操作。

  • diSet:已经发生变化,针对old值
  • willSet:即将发生变化,针对new值
swift 复制代码
struct Town {
    static let region = "South"
    var population = 5_422 {
        didSet(oldPopulation) {
            print("The population has changed to \(population) from \(oldPopulation).")
        }
    }
}    

//下面的代码就会触发didSet
fredTheZombie.victimPool = 500

静态不可变属性

在swift中又称为类型属性,即不能过实例就可直接访问。所有的类型属性必须有默认值,可简单理解为swift中的类型属性就是java中的final类型。

  • 结构体中使用
swift 复制代码
struct Town {
    static let region = "South"
}
  • 类的类型属性
swift 复制代码
class Monster {
    class var spookyNoise: String {
        return "Grrr"
    }
    static let isTerrifying = true
}    
//测试
print(Monster.region)
print(Monster.isTerrifying)

这个例子中使用了计算属性,所以在子类中可以允许再覆写的,但需要用到关键字override。在子类中覆写:

swift 复制代码
class Zombie: Monster {
    //覆写
    override class var spookyNoise: String {
        return "Brains..."
    }
}

访问作用域控制

有时候,我们希望程序的某部分代码对其他部分不可见。事实上,对代码访问有细粒度的控制是常见需求。我们可以给某些组件访问其他组件的一定级别的访问权限,这被称为访问控制(access control)。

访问控制是围绕着模块和源代码而言的,在Swift中提供了5种访问控制符号

访问层级 描述 对...可见 能在...继承
open 实体对模块内的所有文件以及引入了此模块的文件都可见,并且可以继承 实体所在的模块及引入、实体所在模块的模块 实体所在的模块及引入、实体所在模块的模块
public 对模块内的所有文件以及引入了此模块的文件都可见 实体所在的模块及引入、实体所在模块的模块 实体所在模块
internal(默认) 实体对模块内的所有文件可见 实体所在模块 实体所在模块
fileprivate 实体只对所在的源文件可见 实体所在的文件 实体所在的文件
private 实体只对所在的作用域可见 所在的作用域 所在的作用域

修饰属性示例

下面是一个简单的示例:

swift 复制代码
private var isFallingApart = false

修饰属性读写方法的示例,这里的set可有可无,如果设置了set则说明private只对set方法有效,而get方法则采用默认修饰符,所以为了麻烦一般都不指定set。get和set是自定义的方法函数名。

swift 复制代码
class Zombie: Monster {
    override class var spookyNoise: String {
        return "Brains..."
    }
    
    var walksWithLimp = false
    private(set) var isFallingApart = false
    
    final override func terrorizeTown() {
        if !isFallingApart {
            town?.changePopulation(by: -10)
        }
    }
}
相关推荐
2401_8581205341 分钟前
Spring Boot框架下的大学生就业招聘平台
java·开发语言
转调1 小时前
每日一练:地下城游戏
开发语言·c++·算法·leetcode
Java探秘者1 小时前
Maven下载、安装与环境配置详解:从零开始搭建高效Java开发环境
java·开发语言·数据库·spring boot·spring cloud·maven·idea
2303_812044461 小时前
Bean,看到P188没看了与maven
java·开发语言
秋夫人1 小时前
idea 同一个项目不同模块如何设置不同的jdk版本
java·开发语言·intellij-idea
不穿格子衬衫2 小时前
常用排序算法(下)
c语言·开发语言·数据结构·算法·排序算法·八大排序
萧鼎2 小时前
Python调试技巧:高效定位与修复问题
服务器·开发语言·python
wdxylb2 小时前
使用C++的OpenSSL 库实现 AES 加密和解密文件
开发语言·c++·算法
Geek之路2 小时前
QT系统学习篇(1)
开发语言·qt·学习