[TOC]
协议
协议中全是抽象概念(只有声明没有实现), 遵循协议的类可以各自对协议中的计算属性和方法给出自己的实现版本 ,这样当我们面向协议编程时就可以把多态的优势发挥到淋漓尽致 ,可以写出更通用更灵活的代码(符合开闭原则)
实现开闭原则最关键有两点:
- 抽象是关键(在设计系统的时候一定要设计好的协议);
- 封装可变性(桥梁模式 - 将不同的可变因素封装到不同的继承结构中)
协议是方法的集合(计算属性相当于就是方法), 可以把看似不相关的对象的公共行为放到一个协议中。
协议在Swift开发中大致有三种作用:
- 能力 - 遵循了协议就意味着具备了某种能力
- 约定 - 遵循了协议就一定要实现协议中的方法
- 角色 - 一个类可以遵循多个协议, 一个协议可以被多个类遵循, 遵循协议就意味着扮演了某种角色, 遵循多个协议就意味着可以扮演多种角色
创建协议
protocol Flyable {
func fly()
}
protocol Fightable {
func fight()
}
协议的实现(一个类可以继承多个协议)
class Superman: Fightable,Flyable {
func fight() {
print("超人正在打怪兽.")
}
func fly() {
print("超人使用超能力飞行.")
}
}
协议扩展 - 可以在协议扩展中给协议中的方法提供默认实现
也就是说如果某个类遵循了协议但是没有实现这个方法就直接使用默认实现
那么这个方法也就相当于是一个可选方法(可以实现也可以不实现)
extension Fightable {
func fight() {
print("正在打架")
}
}
协议的继承(协议可以继承多个协议)
protocol NiuBi: Flyable, Fightable {
func dive()
}
协议的组合
let array: [protocol<Flyable, Fightable>] = [
Superman()
]
for obj in array {
obj.fly()
obj.fight()
}
运行结果
依赖倒转原则(面向协议编程)
- 声明变量的类型时应该尽可能使用协议类型
- 声明方法参数类型时应该尽可能使用协议类型
- 声明方法返回类型时应该尽可能使用协议类型
结构
Swift中的类和结构体定义的语法是非常相似的。类使用class关键词定义类,使用struct关键词定义结构体,它们的语法格式如下:
class 类名 {
定义类的成员
}
struct 结构体名 {
定义结构体的成员
}
结构与类的区别:
1: 结构的对象是值类型, 类的对象是引用类型
- 值类型在赋值的时候会在内存中进行对象的拷贝
- 引用类型在赋值的时候不会进行对象拷贝只是增加了一个引用
** 结论**: 我们自定义新类型时优先考虑使用类而不是结构除非我们要定义的是一种底层的数据结构(保存其他数据的类型)
2: 结构会自动生成初始化方法
3: 结构中的方法在默认情况下是不允许修改结构中的属性除非加上mutating关键字
struct Student {
var name: String
var age: Int
func study(courseName: String) {
print("\\(name)正在学习.")
}
mutating func getOlder() {
age += 1
}
}
委托
有的时候某个对象要做某件事情但其自身又没有能力做这件事情
这个时候就可以使用委托回调的编程模式让别的对象来做这件事情
以考试找枪手代考为例
实现委托回调的编程模式有以下几个步骤:
1 设计一个协议(被委托方必须要遵循协议才能给别的对象当委托)
protocol ExamDelegate: class {
func answerTheQuestion()
}
class Student {
var name: String
weak var delegate: ExamDelegate?//2委托方添加一个属性其类型是遵循了协议的被委托方
init(name: String) {
self.name = name
}
func joinExam() {
print("姓名: \\(name)")
delegate?.answerTheQuestion()
}
}
3自己做不了的事情委托给别的对象来做
class Gunman: ExamDelegate {//4让类遵循协议成为被委托方(协议表能力)
func answerTheQuestion() {//5 遵循协议就必须要实现协议中的方法(协议表约定)
print("奋笔疾书各种答案")
}
}
6它遵循了协议所以有充当委托的能力也就是说可以扮演被委托方的角色
let stu = LazyStudent(name: "老王")
let gun = Gunman()
stu.delegate = gun
stu.joinExam()
运行结果
内存管理
Swift 是自动管理内存的,这也就是说,我们不再需要操心内存的申请和分配。当我们通过初始化创建一个对象时,Swift 会替我们管理和分配内存。而释放的原则遵循了自动引用计数 (ARC) 的规则:当一个对象没有引用的时候,其内存将会被自动回收。这套机制从很大程度上简化了我们的编码,我们只需要保证在合适的时候将引用置空 (比如超过作用域,或者手动设为 nil 等)
如果程序中出现了类与类之间双向关联关系 必须要将其中一端设置为weak引用 否则将会形成循环引用导致ARC无法释放内存
推荐使用
- 如果允许使用可空类型通常使用weak来破除循环引用
- 如果员工关联的部门对象被释放了那么dept会被赋值为nil
- 如果要继续给dept对象发消息程序不会崩溃
谨慎使用
- 如果不允许使用可空类型就必须使用unowned来破除循环引用
- 需要注意的是如果员工对象关联的部门对象被释放了
- 如果还要通过员工对象去操作它所关联的部门对象将导致程序崩溃(EXC_BAD_ACCESS)
员工与部门为例
class Emp {
weak var dept: Dept?//weak 破除循环
init(dept: Dept) {
print("创建一个员工")
self.dept = dept
}
deinit { //在销毁对象的时候调用
print("销毁一个员工")
}
}
class Dept {
var manager: Emp?
init() {
print("创建一个部门")
}
deinit {
print("销毁一个部门")
}
}
func bar() {
let dept = Dept()
let emp = Emp(dept: dept)
dept.manager = emp
}
bar()
泛型
泛型 (generic) - 让类型不再是程序中的硬代码, Swift中的类、结构和枚举都可以使用泛型
泛型限定<T: Comparable>限定T类型必须是遵循了Comparable协议的类型
定义一个虚拟类型T, 调用函数时根据传入的参数类型来决定T到底是什么
func myMin<T: Comparable>(a: T, _ b: T) -> T {
return a < b ? a : b
}
class Student: Comparable {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
func ==(one: Student, two: Student) -> Bool {
return one.name == two.name
}
func <(one: Student, two: Student) -> Bool {
return one.name < two.name
}
func <=(one: Student, two: Student) -> Bool {
return one.name <= two.name
}
func >(one: Student, two: Student) -> Bool {
return one.name > two.name
}
func >=(one: Student, two: Student) -> Bool {
return one.name >= two.name
}
var stu1 = Student(name: "小强", age: 35)
var stu2 = Student(name: "狒狒", age: 18)
let minStu = myMin (stu1,stu2)
print(minStu.age)
异常处理
定义一个遵循ErrorType协议的枚举
通过不同的case定义程序中可能出现的若干种异常状况
enum FractionError: ErrorType {
case ZeroDenominator // 分母为0
case DivideByZero // 除以0
}
以分数为例
class Fraction {
private var _num: Int
private var _den: Int
var info: String {
get {
return _num == 0 || _den == 1 ? "\\(_num)" : "\\(_num)/\\(_den)"
}
}
// 如果一个方法抛出了异常 那么在声明方法时必须要写上throws关键字
// throws关键字是提醒方法的调用者方法可能会出状况 调用时要写try
init(num: Int, den: Int) throws {
_num = num
_den = den
if _den == 0 {
// 如果程序中出现问题就抛出错误(异常)
// 被throw关键字抛出的必须是遵循ErrorType协议的东西
throw FractionError.ZeroDenominator
}
else {
simplify()
normalize()
}
}
func add(other: Fraction) -> Fraction {
// 如果能够确保方法调用时不出异常那么可以在try关键字后加!
// 这样就可以在不写do...catch的情况下调用可能出状况的方法
return try! Fraction(num: _num * other._den + other._num * _den, den: _den * other._den)
}
func sub(other: Fraction) -> Fraction {
return try! Fraction(num: _num * other._den - other._num * _den, den: _den * other._den)
}
func mul(other: Fraction) -> Fraction {
return try! Fraction(num: _num * other._num, den: _den * other._den)
}
func div(other: Fraction) throws -> Fraction {
if other._num == 0 {
throw FractionError.DivideByZero
}
return try! Fraction(num: _num * other._den, den: _den * other._num)
}
func normalize() -> Fraction {
if _den < 0 {
_num = -_num
_den = -_den
}
return self
}
func simplify() -> Fraction {
if _num == 0 {
_den = 1
}
else {
let x = abs(_num)
let y = abs(_den)
let g = gcd(x, y)
_num /= g
_den /= g
}
return self
}
}
运算符重载(为自定义的类型定义运算符)
func +(one: Fraction, two: Fraction) -> Fraction {
return one.add(two)
}
func -(one: Fraction, two: Fraction) -> Fraction {
return one.sub(two)
}
func *(one: Fraction, two: Fraction) -> Fraction {
return one.mul(two)
}
func /(one: Fraction, two: Fraction) throws -> Fraction {
return try one.div(two)
}
如果能够保证代码不出错可以在try后面加!
如果不确定代码是否出错可以在try后面加?
需要注意的是有?的地方会产生Optional(可空类型)
稍后可能还需要对可空类型进行拆封, 拆封方式有二:
- 不安全的做法: xxx!
- 安全的做法: 用if let = xxx { }进行拆封
例
func foo() {
let f1 = try? Fraction(num: 3, den: 0)
let f2 = try? Fraction(num: 0, den: 9)
if let a = f1, b = f2 {
let f3 = a + b
print(f3.info)
}
else {
print("无效的分数无法进行加法运算")
}
}
foo()
对于可能出状况的代码要放在do...catch中执行
在可能出状况的方法前还要写上try表示尝试着执行
如果在do中没有出现任何状况那么catch就不会执行
如果do中出现了状况代码就不会再向下继续执行而是转移到catch中
在do的后面可以跟上多个catch用于捕获不同的异常状况 但是最多只有一个catch会被执行
例
do {
let f1 = try Fraction(num: 3, den: 4)
let f2 = try Fraction(num: 0, den: 9)
print(f1.info)
print(f2.info)
let f3 = f1 + f2
print(f3.info)
let f4 = f1 - f2
print(f4.info)
let f5 = f1 * f2
print(f5.info)
let f6 = try f1 / f2
print(f6.info)
}
catch FractionError.ZeroDenominator {
print("分母不能为0!!!")
}
catch FractionError.DivideByZero {
print("除以0是不行的!!!")
}
catch {//其他错误
print("出错了! 我也不知道什么问题")
}