Swift(四 面向对象OOP特征)
学习不像爱情。爱情你一认真就输了。学习你一认真就赢了。
A plant may produce new flowers; man is young but once.
(花有重开日,人无再少年)
-
OOP: Object Oriented Programming 面向对象的程序设计
-
Swift语言中,不仅类具有面向对象的特征,结构体与枚举都具有面向对象的特征。但是类是引用类型,结构体与枚举是值类型(作为参数需要修改内容时有区别,类直接传对象 其他传地址&)。
-
类的“实例”一般称为“对象”,但是,枚举与结构体的“实例”不能称之为“对象”,因为它们不是真正的面向对象类型。只是包含了面向对象的特点。
1、枚举 - 值类型
-
枚举 = 成员值 + 相关值 (成员值 后 可加 原始值)
-
成员值
-
定义 - 枚举的成员值 默认情况下 不是整数类型0,1,...
enum WeekDays { case Monday case Tuesday case Wednesday case Thursday case Friday // 注意:如果枚举成员类型不同这样写case c(Character) } // 简易写法 enum weekDays: Int{ case Monday, Tuesday, Wednesday, Thursday, Firday }
-
赋值
var day = weekDays.Monday // 枚举类型名称.成员值 day = .Monday // .成员值 能这么用的前提:必须有上下文,Swift能判断出类型 switch day { case .Monday: print("Monday") case .Tuesday: print("Tuesday") case .Wednesday: print("Wednesday") case .Thursday: print("Thursday") case .Firday: print("Firday") // default: // print("default") /** default分支在枚举中可以省略,使用其他类型是不允许省略的。 只要枚举中case列举完毕所有可能值, default可省略,不省略有warning。 如果 存在default同时不存在warning,那么可以把case .Firday 分支去掉 (其实去掉任何一个分支都可以,只要满足所有可能性全包括了即可)*/ }
-
-
原始值(raw value)
-
可以是字符、字符串、整数、浮点数等
// 1、加入数据类型Int 2、加入默认值0,1,2,... enum weekDays: Int{ // 之后自动加1。也可以case Monday = 0 case Tuesday = 1...分开写,也可只给第一个成员赋值,后面成员值会依次+1 case Monday = 0, Tuesday, Wednesday, Thursday, Firday } let friday = weekDays.Firday.rawValue // 4 成员值->原始值 let thursday = weekDays(rawValue: 3) // Thursday 原始值->成员值,是 调用枚举的 构造函数 初始化 枚举的实例。
-
-
相关值
/** 枚举的相关值 枚举中成员值 相关值 一般首字母大写,就像OC中的枚举前面也是大写(前缀是大写的) */ enum Figure{ case Rectangle(Int, Int) case Circle(Int) } func printFigure(figure: Figure){ switch figure { /** 自带值的提取!! 如果某个相关值元组中字段类型一致,需要全部提取, 可以将字段前面的let或者var移到相关值前面。 case let .Rectangle(width, height): case let .Circle(radius): 字段是一个的也可以,但是没有必要 */ case .Rectangle(let width, let height): print("矩形的宽\(width)高\(height)") case .Circle(let radius): print("圆的半径\(radius)") } } printFigure(figure: Figure.Rectangle(2, 1)) printFigure(figure: Figure.Circle(2)) // 打印 矩形的宽2高1 圆的半径2
2、结构体 - 值类型
-
Swift重视结构体,把结构体作为实现面向对象的重要手段。不仅可以定义成员变量也就是属性,还可以定义成员方法。可以看作是一种轻量级的类。(而其他语言中的结构体 只能定义一组相关的成员变量)
-
结构体的定义 : struct + 结构体名称{ 成员变量 }
// 部门(下文要用到此结构体实例化) - 部门与员工是1 : n的关系(1 对 多的关系) struct Department{ var no: Int = 0 var name: String = "" }
-
结构体的实例化--如果要给结构体的成员变量赋值,用var不用let。而类一般用let
var dept1 = Department() // 有属性赋值必须是var dept1.no = 10 dept1.name = "Sales"
3、类 - 引用类型
-
类的定义
// 员工类(下文要用此类实例化) - 员工与部门是n : 1的关系 class Employee{ var no: Int = 0 var name: String = "" var job: String? var salory: Double = 0.0 var dept: Department? // 结构体类型所在部门属性 }
-
类的实例化--类一般声明为let常量,由于类是引用数据类型,声明为let常量只是说明不能修改引用(比如下面的emp1),但是可以通过内存地址拿到对应的对象,修改对象内部的属性
let emp1 = Employee() emp1.no = 1000 emp1.name = "Martin" emp1.job = "Salesman" emp1.salory = 1250
4、 值类型与引用类型
-
类是引用类型,其他类型全是值类型!!(再记一遍,重要)
引用类型 与 值类型 修改内容举例:// 创建实例 var dept = Department() // var dept.no = 10 dept.name = "Sales" let emp = Employee() emp.no = 1000 emp.name = "Martin" emp.job = "Salesman" emp.salory = 1250 ================= 值类型分割线 ================ #warning 值类型 /** 不加inout,传入dept,编译失败,修改不成功,说明了结构体是值类型, 引用类型不用加inout 传入引用对象即可修改成功 */ func updateDept(dept: inout Department){ dept.name = "Research" } print("Deparment更新前\(dept.name)") // "Sales" updateDept(dept: &dept) print("Deparment更新后\(dept.name)") // "Research" ================= 引用类型分割线 ================ #warning 引用类型 func updateEmp(emp: Employee){ emp.job = "Clerk" } print("Employment更新前\(emp.job!)") // "Salesman" job是可选类型 需要解包 updateEmp(emp: emp) print("Employment更新后\(emp.job!)") // "Clerk"
5、引用类型的比较
-
对于引用类型的实例比较,比如类的实例比较 只能用=== 与 !==
-
结构体与类实例化
var dept1 = Department() // var 结构体 dept1.no = 10 dept1.name = "Sales" var dept2 = Department() // var 结构体 dept2.no = 10 dept2.name = "Sales" let emp1 = Employee() // 对象 emp1.no = 1000 emp1.name = "Martin" emp1.job = "Salesman" emp1.salory = 1250 let emp2 = Employee() // 对象 emp2.no = 1000 emp2.name = "Martin" emp2.job = "Salesman" emp2.salory = 1250
-
类实例比较 & 结构体属性的比较
// 比较是否实例相等 if emp1 !== emp2{ // false : 不同的对象 print("emp1 !== emp2") } if emp1 === emp1 { // true : 同一个对象 print("emp1 === emp1") } if dept1.no == dept2.no { // true : 值相等 print("dept1.no == dept2.no") }
-
结构体的实例比较 - 编译失败
if dept1 == dept2 { // 编译失败:对于枚举 与结构体 直接比较编译失败,需要运算符重载 print("dept1 == dept2") }else{ print("dept1 != dept2") }
-
运算符重载 - 编译成功
// 运算符重载:每对属性分别比较。调用d1 == d2固定调用写法 func ==(d1: Department, d2: Department) -> Bool{ return d1.no == d2.no && d1.name == d2.name } func !=(d1: Department, d2: Department) -> Bool{ return d1.no != d2.no || d1.name != d2.name } 打印结果: dept1 == dept2
注明: 判断中dept1 == dept2语句调用该运算符的重载函数,对于有多个判断是否相等的重载函数,应该调用哪个重载函数由重载函数的参数类型(如:Department)决定。
-
6、 类型嵌套
-
关于类型嵌套,它会使程序结构变得不清晰,可读性差。
-
优点:支持访问外部类的成员,成员包括:方法,属性,枚举,结构体等嵌套类型
-
访问:对象名.属性名,对象名.枚举实例,对象名.结构体实例,类名.枚举名.枚举成员值(.rawValue),
不能对象名.枚举名,对象名.结构体名// 类型嵌套的使用 class Employee{ var no: Int = 0 var name: String = "" var job: String? var salary: Double = 0 var dept: Department = Department() var day: WeekDays = WeekDays.Friday struct Department { // 里一层 var no: Int = 10 var name: String = "SALES" } enum WeekDays { // 里一层 case Monday case Tuesday case Wednesday case Thursday case Friday struct Day { // 里二层 static var message: String = "Today is..." } } } ============= 实例化分割线 ========== var emp = Employee() print(emp.dept.name) print(emp.day) // 对象emp不能调用结构体 枚举等名 let friday = Employee.WeekDays.Friday if emp.day == friday { print("相等") } print(Employee.WeekDays.Day.message) // 一层嵌套一层。类名称只能直接调用枚举名称 结构体名称 //print(Employee.WeekDays.Day) 编译失败, 括号内必须是字符串,枚举没有成员默认值
7、可选链
-
可选链是在类型嵌套中常用的。比如外部类Employee有个属性不确定有没有值的存在(eg: var dept: Department?),这个属性是个嵌套类类型(成员有var comp: Company?),Company又是一个嵌套类类型(成员有var name: String = "张三")。
-
如果要访问name,会这么做:
let emp = Employee() print(emp.dept!.comp!.name) // 显式拆包 // 但是显式拆包有个弊端,如果可选链中某个环节为nil,将会导致代码运行错误 // fatal error: unexpectedly found nil while unwrapping an Optional value // (之前基本数据类型赋值对于nil是用可选绑定解决的) print(emp.dept?.comp?.name) // 可选链 // 如果某个环节为nil,不会抛出异常,会把nil返回给引用者
注意:在可选链中,最后的字段不能再加问号,会报错:error: '?' must be followed by a call, member lookup, or subscript