Swift第四周(第一月)总结

沉迷学习,日渐消瘦,一天不学习,浑身难受。

学习Swift已过一个月,已经从零基础到入门,能够逐渐理顺思绪,找到关键点。

swift副本.jpg

协议

  • 协议是方法的集合(计算属性相当于就是方法),可以把看似不相关的对象的公共行为放到一个协议中

注释: 协议在Swift开发中大致有三种作用:

  1. 能力 - 遵循了协议就意味着具备了某种能力
  2. 约定 - 遵循了协议就一定要实现协议中的方法
  3. 角色 - 一个类可以遵循多个协议, 一个协议可以被多个类遵循, 遵循协议就意味着扮演了某种角色, 遵循多个协议就意味着可以扮演多种角色
  • Swift中的继承是单一继承(一个类只能有一个父类), 如果希望让一个类具备多重能力可以使用协议来实现(C++里面是通过多重继承来实现的, 这是一种非常狗血的做法)

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

  • 实现开闭原则最关键有两点:

  1. 抽象是关键(在设计系统的时候一定要设计好的协议);
  2. 封装可变性(桥梁模式 - 将不同的可变因素封装到不同的继承结构中)

注释:接口(协议)隔离原则: 协议的设计要小而专不要大而全
协议的设计也要高度内聚

protocol Flyable {
    
    func fly()
}
protocol Fightable {
    
    func fight()
}

协议扩展

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

extension Fightable {
    
    func fight() {
        print("正在打架")
    }
}
// 协议的继承
protocol NiuBi: Flyable, Fightable { 
    func dive()
}
class Boxer: Fightable {
    
    @objc func fight() {
        print("正在进行格斗.")
    }
}
  • 依赖倒转原则(面向协议编程)
  1. 声明变量的类型时应该尽可能使用协议类型
  2. 声明方法参数类型时应该尽可能使用协议类型
  3. 声明方法返回类型时应该尽可能使用协议类型
et x: protocol<Flyable, Fightable> = Superman()
let y: NiuBi = Superman()
  • 协议组合
let array: [protocol<Flyable, Fightable>] = []

计算机属性:

计算机的硬件由五大部件构成:
运算器、控制器、存储器、输入设备、输出设备
运算器 + 控制器 => CPU (中央处理器)
存储器 => 内存 (RAM - Random Access Memory)

程序员可以使用的内存大致分为五块区域:

栈 (stack) - 我们定义的局部变量/临时变量都是放在栈上

  • 特点: 小、快
    堆 (heap) - 我们创建的对象都是放在堆上的
  • 特点: 大、慢
    静态区 (static area)
  • 数据段 - 全局量
  • 只读数据段 - 常量
  • 代码段 - 函数和方法
值和类型的区别:
  • 区别1: 结构的对象是值类型, 类的对象是引用类型
    值类型在赋值的时候会在内存中进行对象的拷贝
    引用类型在赋值的时候不会进行对象拷贝只是增加了一个引用

** 结论:** 我们自定义新类型时优先考虑使用类而不是结构除非我们要定义的是一种底层的数据结构(保存其他数据的类型)

引用类型的类
let stu1 = Student1(name: "骆昊", age: 35)
var stu3 = stu1     // 此处内存中仍然只有一个学生对象
stu3.name = "罗小号"
stu3.age = 18
print(stu1.name)
print(stu1.age)
  • 值类型的结构
    区别2: 结构会自动生成初始化方法
et stu2 = Student2(name: "骆昊", age: 35)
var stu4 = stu2     // 此处内存中会复制一个新的学生对象
stu4.name = "王大锤"
stu4.age = 18
print(stu2.name)
print(stu2.age)
  • 区别3: 结构中的方法在默认情况下是不允许修改结构中的属性除非加上mutating关键字:
mutating func getOlder() {
        age += 1
    }
}
  • 其他:
在Swift中同名函数只要参数列表不同是可以共存的 这个叫函数的重载
func changeName(inout name: String) {
    name = "王大锤"
}

协议委托回调:

有的时候某个对象要做某件事情但其自身又没有能力做这件事情,这个时候就可以使用委托回调的编程模式让别的对象来做这件事情

实现委托回调的编程模式有以下几个步骤:

  1. 设计一个协议(被委托方必须要遵循协议才能给别的对象当委托)
protocol CanvasDelegate: class {

协议里面的方法就是要委托其他对象做的事情

unc showMessage(canvas: Canvas, message: String)
}
  1. 委托方添加一个属性其类型是遵循了协议的被委托方
class Canvas: UIView {
    // 2. 委托方添加一个属性其类型是遵循了协议的被委托方
     weak var delegate: CanvasDelegate?
     var renjuBoard = RenjuBoard()
    var isAutoMode = false
    func clearBoard() {
        renjuBoard.reset()
        setNeedsDisplay()
    }
    func randomMove() {
        let row = Int(arc4random_uniform(15))
        let col = Int(arc4random_uniform(15))
        if renjuBoard[row, col] {
            renjuBoard[row, col] = renjuBoard.isBlackTurn
            setNeedsDisplay()
        }
    }
  1. 自己做不了的事情委托给别的对象来做
delegate?.showMessage(self, message: renjuBoard.isBlackTurn ? "白棋胜" : "黑棋胜")

其他:

 Swift 2中的guard大法, Swift 3中据说要废掉
        guard !isAutoMode else { return }
        // guard !renjuBoard.isGameOver else { return }
  • 索引器:
    // 索引器语法 - 可以直接对棋盘对象做下标运算来放置棋子
    subscript(row: Int, col: Int) -> Bool {
        get { return board[row][col] == .Space }
        set(isBlack) {
            if board[row][col] == .Space {
                board[row][col] = isBlack ? .Black : .White
                isBlackTurn = !isBlackTurn
            }

指派构造器:

指派构造器(designated)
    required init(name: String, age: Int) {
        print("创建一个人!")
        self.name = name
        self.age = age
    }

注释:指派构造器前面加上required可以将构造器指定为必要构造器
所谓的必要构造器意味着子类也要提供一模一样的构造器

  • 下面的语句必须写在调用自己的初始化方法之后否则major属性会被赋上不正确的值:
        self.major = major
        self.init(name: name, age: age)
        self.major = major
        初始化的第一阶段
          1. 初始化自己特有的属性
       self.major = major
       // 子类只能调用直接父类的构造器
        // 子类构造器必须调用父类的非便利构造器(指派构造器)
 super.init()    // compiler error
        //  2. 调用父类的初始化方法
        super.init(name: name, age: age)
 //初始化的第二阶段
       // 此处可以调用对象的方法因为对象已经完成了初始化
        study()
    }
      func study() {
        print("\(name)正在学习.")
    }
     deinit {
        print("学生对象嗝屁了!")
    }
}
class Teacher: Person {
    deinit {
        print("老师对象嗝屁了!")
    }
}
  • 栈 - FILO 先进后出的结构
    创建任何子类对象的时候一定是先创建了父类对象
    var stu: Person = Student()
    引用转移(会导致原来对象上的引用计数-1 新对象引用计数+1)
 stu = Teacher()
 stu = Person()
  • Swift的自动释放池
    通过向autoreleasepool函数中传入一个闭包来实现
    autoreleasepool { () -> () in
    自动释放池中的对象引用在池的边界会收到引用计数-1的消息
> 将来做iOS开发时如果某个地方会创建很多的临时对象.   那么最好在此处设置一个自动释放池避免内存瞬时峰值过高造成闪退
     let stu1 = Student()
     let stu2 = stu1
 }

离开自动释放池时 stu1会收到引用计数-1消息 stu2也会收到引用计数-1消息

  • 如果程序中出现了类与类之间双向关联关系 必须要将其中一端设置为weak引用,否则将会形成循环引用导致ARC无法释放内存

注释:
推荐使用
如果允许使用可空类型通常使用weak来破除循环引用
如果员工关联的部门对象被释放了那么dept会被赋值为nil
如果要继续给dept对象发消息程序不会崩溃
weak var dept: Dept?

 谨慎使用
 如果不允许使用可空类型就必须使用unowned来破除循环引用
 需要注意的是如果员工对象关联的部门对象被释放了
 如果还要通过员工对象去操作它所关联的部门对象将导致程序崩溃
 EXC_BAD_ACCESS

泛型 (generic)

  • 让类型不再是程序中的硬代码(写死的东西)
    1.定义虚拟类型:
// 定义一个虚拟类型T, 调用函数时根据传入的参数类型来决定T到底是什么
func mySwap<T>(inout a: T, inout _ b: T) {
    let temp = a
    a = b
    b = temp
}

2.泛型限定

// <T: Comparable>限定T类型必须是遵循了Comparable协议的类型
func myMin<T: Comparable>(a: T, _ b: T) -> T {
    return a < b ? a : b
}

注释:wift中的类、结构和枚举都可以使用泛型:

struct Stack<T> {
    var data: [T] = []
    
    // 入栈
    mutating func push(elem: T) {
        data.append(elem)
    }
    
    // 出栈
    mutating func pop() -> T {
        return data.removeLast()
    }
    
    var isEmpty: Bool {
        get { return data.count == 0 }
    }
}

抛错误异常

  • 如果一个方法抛出了异常 那么在声明方法时必须要写上throws关键字
    throws关键字是提醒方法的调用者方法可能会出状况 调用时要写try:
init(num: Int, den: Int) throws {
        _num = num
        _den = den
        if _den == 0 {
            // 如果程序中出现问题就抛出错误(异常)
            // 被throw关键字抛出的必须是遵循ErrorType协议的东西
            throw FractionError.ZeroDenominator
        }
        else {
            simplify()
            normalize()
        }
    }

But:
1.如果能够确保方法调用时不出异常那么可以在try关键字后加!
2.这样就可以在不写do...catch的情况下调用可能出状况的方法

注释:如果能够保证代码不出错可以在try后面加!
1.如果不确定代码是否出错可以在try后面加?
2.需要注意的是有?的地方会产生Optional(可空类型)
稍后可能还需要对可空类型进行拆封, 拆封方式有二:
1. 不安全的做法: xxx!
2. 安全的做法: 用if let = xxx { }进行拆封

    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

注释:
1.对于可能出状况的代码要放在do...catch中执行.
2.在可能出状况的方法前还要写上try表示尝试着执行
3.如果在do中没有出现任何状况那么catch就不会执行
4.如果do中出现了状况代码就不会再向下继续执行而是转移到catch中
5.在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!!!")

总结:

相对于这几周的语法相对于比较枯燥和难一些,攻破了这个之后的路就能更为顺畅一些,在之后的日子更要努力。

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,633评论 18 139
  • day one 依赖倒转原则(面向协议编程) 声明变量的类型时应该尽可能使用协议类型 声明方法参数类型时应该尽可能...
    saman0阅读 363评论 0 0
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,598评论 18 399
  • 1.项目经验 2.基础问题 3.指南认识 4.解决思路 ios开发三大块: 1.Oc基础 2.CocoaTouch...
    阳光的大男孩儿阅读 4,971评论 0 13
  • Date: 20th Nov, 2016 Location: Veolia 阳光热电,哈尔滨 Holder: Ve...
    io晶晶阅读 241评论 0 0