Swift第三周学习总结

协议

1.协议:协议只给出方法的声明,不给出具体方法的实现过程,协议是方法的集合(计算属性相当于就是方法),谁遵循协议就要定义方法。
2.协议在Swift中的作用:

  • 1.能力 - 遵循了协议就意味着具备了某种能力
  • 2.约定 - 遵循了协议就一定要实现协议中的方法
  • 3.角色 - 一个类可以遵循多个协议,一个协议也可以被多个类遵循,遵循协议就意味着扮演了某种角色,遵循了多个协议就意味着可以扮演多个角色

3.协议与继承的区别在于:Swift中的继承是单一继承(一个类只能有一个父类),如果希望让一个类具有多重能可以使用协议来实现。


例:我们先建一个Father的类,这个类有吃、喝、嫖、赌四种行为(方法)

class Father {
    func eat(){
        
    }
    
    func drink(){
        
    }
    
    func wench(){
        
    }
    
    func gamble(){
        
    }
}

然后我们再创建一个Monk的类,和一个Musician的类

protocol Monk {
    func eatVegitable()
    
    func chant()
    
    func knockTheBell()
}

protocol Musician{
    func playPiano()
    
    func playViolin()
}

最后创建一个Son的类,它继承了Father,并且遵循了Monk、Musician协议

class Son: Father, Monk, Musician {
    override func gamble() {
        print("正在斗地主")
    }
    func eatVegitable() {
    }
    
    func chant() {
    }
    
    func knockTheBell() {
    }
    
    func playPiano() {
    }
    
    func playViolin() {
    }
    
    func steal(){
    }
}

这个Son类继承于Father当然就拥有了它的属性,同时它遵循了Monk、Musician协议,就拥有了它们的能力,但前提是必须要实现它们的方法。
所以当我们创建一个Son的对象它就拥有了如下的方法:


屏幕快照 2016-08-19 下午8.09.54.png

4.协议的继承
5.协议的组合

let array: [protocol<Monk,Musician>] = [Son()]

当然协议也有多态。
6.协议的扩展
协议扩展 - 可以在协议扩展中给协议中的方法提供默认实现
也就是说如果某个类遵循了协议但是没有实现这个方法就直接使用默认实现
那么这个方法也就相当于是一个可选方法(可以实现也可以不实现)

extension Fightable{
    func fight(){
        print("正在打架.")
    }
}

总结:协议中全是抽象概念(只有声明没有实现),遵循协议的类可以各自对协议中的计算属性和方法给出自己的实现版本,这样当我们面向协议编程时就可以把多态的优势发挥到淋漓尽致,可以写出更通用更灵活的代码(符合开闭原则)

  • 实现开闭原则最关键有两点:
    • 1.抽象是关键(在设计系统的时候一定要设计好协议);
    • 2.封装可变性(桥梁模式- 将不同的可变因素封装到不同的继承结构中)
    • 3.接口(协议)隔离原则:协议的设计一定要小而专不要大而全,协议的设计也要高内聚

7.委托回调和代理模式

关于这两种方法我们可以举一个考生找枪手代考的例子:

//枪手代考: 委托回调
protocol ExamDelegate: class {
    
    func answerTheQuestion()
}

class LazyStudent {
    var name: String
    weak var delegate: ExamDelegate? //委托方添加一个属性,其类型是遵循了协议的被委托方
    
    init(name: String) {
        self.name = name
    }
    
    func joinExam() {
        print("姓名: \(name)")
        delegate?.answerTheQuestion()
    }
}

class Gunman: ExamDelegate {
    
    func answerTheQuestion() {
        print("奋笔疾书各种答案")
    }
}

let stu = LazyStudent(name: "王大锤")
let gun = Gunman()
stu.delegate = gun
stu.joinExam()

//枪手代考: 代理模式
protocol ExamCandidate: class {
    
    func answerTheQuestion()
}

class LazyStudent: ExamCandidate {
    var name: String
    
    init(name: String) {
        self.name = name
    }
    
    func answerTheQuestion() {
    }
}

class Gunman: ExamCandidate {
    var name: String
    var target: LazyStudent? //代理方添加一个属性,其类型是被代理方
    
    init(name: String) {
        self.name = name
    }
    
    func answerTheQuestion() {
        if let stu = target {
            print("姓名: \(stu.name)")
            print("奋笔疾书答案")
            print("提交试卷")
        }
    }
}

let stu = LazyStudent(name: "王大锤")
let gun = Gunman(name: "骆昊")
gun.target = stu
gun.answerTheQuestion()

** 注意:**就学生和枪手这个例子而言,委托回调相当于学生只是带了个作弊器,而代理模式才是真正的枪手帮学生代考,这两种方法还是有一定的区别的。

结构体

1.我们分别创建一个类和一个结构体:

class Student1 {
    var name: String
    var age: Int
    
    init (name: String, age: Int){
        self.name = name
        self.age = age
    }
    func study(courseName: String){
        print("\(name)正在学习.")
    }
}
struct Student2 {
    var name: String
    var age: Int
    
    func study(courseName: String){
        print("\(name)正在学习.")
    }
    mutating func getOlder(){
        age += 1
    }
}

2.创建对象,找出区别

let stu1 = Student1(name: "罗大号", age: 35)
var stu3 = stu1  //此处内存中仍然只有一个学生对象
stu3.name = "罗小号"
stu3.age = 18
print(stu1.name)
print(stu1.age)

执行结果为:

罗小号
18
Program ended with exit code: 0

创建结构体的对象:

let stu2 = Student2(name: "翠花", age: 26)
var stu4 = stu2 //此处内存中复制了一个学生对象
stu4.name = "荷花"
stu4.age = 18
print(stu2.name)
print(stu2.age)

执行结果为:

翠花
26
Program ended with exit code: 0

总结: 区别1:重点:结构的对象是值类型,类的对象是引用类型,值的类型在赋值的时候会在内存中进行对象的拷贝,引用类型在赋值的时候不会进行对象拷贝,只是增加了一个引用.
区别2: 结构会自动生成初始化方法
区别3: 结构中的方法在默认情况下是不允许修改结构中的属性的,除非加上mutating
结论: 我们自定义新类型是优先考虑使用类而不是结构,除非我们要定义的是一种底层的数据结构

自动引用计数(ARC)

1.我们先创建几个类:

class Person {
    init(){
        print("创建一个人")
    }
    
    deinit{
        print("人嗝屁了")
    }
}

class Teacher: Person {
    override init() {
        super.init()
        print("创建一个老师")
    }
    deinit{
        print("老师嗝屁")
    }
    
}
class Student: Person{
    override init() {
        super.init()
        print("创建一个学生对象!")
    }
    
    deinit {
        print("学生对象嗝屁!")
    }
}

说明:1.因为创建对象的方式多种多样,所以可以定义多种初始化方法,对象就可以使用多种初始化方法。
2.子类只能调用直接父类的初始化方法,子类构造器必须调用父类的非便利构造器(指派构造器)

2.//创建一个学生对象,然后用stu1去引用它,所以此时学生对象引用计数为1
var stu1: Student? = Student()
//此处没有创建新的学生对象,原来的学生对象的引用计数+1
var stu2 = stu1 var stu3 = stu1
//学生对象引用计数-1
stu1 = nil
//学生对象引用计数-1
stu2 = nil
//学生对象引用计数-1
//当学生对象引用计数为0时,ARC会自动清理内存释放学生对象
//ARC即时性的内存清理 优于java的Garbage Collection(垃圾回收)
stu3 = nil
//弱(weak)修饰的引用不会导致自动引用计数增加,默认是强引用(会增加引用计数)
weak var stu2 = stu1 weak var stu3 = stu1
3.释放内存的方法

  • 1.如果想释放内存,程序员可以手动将一个引用赋值成nil
    stu1 = nil
  • 2.stu是一个局部变量,在函数调用结束后局部变量就消失了,所以学生对象的引用计数也就变成0了,所以会被ARC释放
func foo(){
    let stu = Student()
    print(stu)
}
foo()

结果如下:

创建一个人
创建一个学生对象!
Day08_17_01.Student
学生对象嗝屁!
人嗝屁了
Program ended with exit code: 0
  • 3.引用转移(会导致原来对象上的引用计数-1,新对象+1)
var stu: Person = Student()
stu = Teacher()
stu = Person()

打印结果如下:

创建一个人
创建一个老师
学生对象嗝屁!
人嗝屁了
创建一个人
老师嗝屁
人嗝屁了
Program ended with exit code: 0

从结果可以看出:创建子类对象的时候一定是先创建了父类对象;栈(FIOL)是先进后出的结构。

  • 4.自动释放池:通过向atuoreleasepool函数传入一个闭包来实现
autoreleasepool { () -> () in
    //自动释放池中的对象引用在池的边界引用计数会收到引用计数-1的消息
    //将来做IOS开发是如果在某个地方会创建很多的临时对象
    //那么最好在此处设置一个自动释放池避免内存瞬时峰值过高
    let stu1 = Student()
    let stu2 = stu1
}
离开自动释放池时 stu1会收到引用计数-1的消息,stu2也会收到引用计数-1的消息

4.类与类之间的循环引用
我们来创建两个类(部门和员工)相互引用:

class Dept{
    weak var manager: Emp?

  init(){
        print("创建一个部门")
    }
    deinit{
        print("销毁一个部门")
    }
}
class Emp {
    var dept: Dept?
    init(){
        print("创建一个员工")
    }
    deinit{
        print("销毁一个员工")
    }
  • 如上形成循环引用,导致ARC无法释放内存

解决方法:1.如果程序中出现了类与类之间的双向关联关系 必须将其中一端设置成weak引用,如果允许使用可空类型通常使用weak来破除循环引用
2.如果不允许使用可空类型就必须使用unowned来破除循环引用

class Emp {
    weak var dept: Dept?
    init(){
        print("创建一个员工")
    }
    deinit{
        print("销毁一个员工")
    }

或者这样:

class Emp {
    unowned var dept: Dept
    init(dept: Dept){
    self.dept = dept
        print("创建一个员工")
    }
    deinit{
        print("销毁一个员工")
    }
//需要注意的是如果员工对象关联的部门对象被释放了  
//如果还要通过员工对象去操作它所关联的部门对象将导致程序崩溃
// EXC_BAD_ACCESS

泛型

1.泛型(generic) - 让类型不再是程序中的硬代码(写死的东西)
2.定义一个虚拟的类型T(也可以用其它字符代替),调用函数是根据传入的参数类型来决定T到底是什么

func mySwap<T>(inout a: T, inout _ b: T){
    (a,b) = (b,a)
}

var x = "hello", y = "good"
mySwap(&x, &y)
print(x,y)  //结果:good hello

3.泛型限定
<T: Comparable>限定T类型必须是遵循了Comparable协议的类型

func myMin<T: Comparable>(a: T, _ b: T) -> T {
    return a > b ? a : b
}

var z = "hello", s = "good"
print(myMin(z, s)) //打印结果:hello

上述代码中虚拟类型T是不可以直接比较的,但是遵循了Comparable协议后就能比较了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • [TOC] 协议 协议中全是抽象概念(只有声明没有实现), 遵循协议的类可以各自对协议中的计算属性和方法给出自己的...
    桧霖阅读 340评论 0 3
  • 1. 集合(Set) (1)集合的定义: (2)集合写法: 举例: (4)对集合的操作: 2. 字典(Dictio...
    老韩在简书阅读 524评论 1 2
  • 集合 存储方式离散,通过hash方式存储。集合中没有重复元素 字典(存放键值对组合的容器) 字典中的每个元素都是由...
    离殇丶阅读 113评论 0 0
  • day one 依赖倒转原则(面向协议编程) 声明变量的类型时应该尽可能使用协议类型 声明方法参数类型时应该尽可能...
    saman0阅读 360评论 0 0
  • ___海阅读 226评论 1 2