美文网首页
Swift第四周(第一月)总结

Swift第四周(第一月)总结

作者: Daniel01 | 来源:发表于2016-08-20 11:17 被阅读0次

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

学习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!!!")

总结:

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

相关文章

  • Swift第四周(第一月)总结

    沉迷学习,日渐消瘦,一天不学习,浑身难受。 学习Swift已过一个月,已经从零基础到入门,能够逐渐理顺思绪,找到关...

  • 7月第四周周总结

    7月第四周周总结 7月第四周周总结 2020年,07:20-07.26周检视 没有反思就没有成长。没有反思的人生不...

  • 突然想到的总结

    这个月就第一周结束时写了周总结,现在已经是第四周了,那我就总结一下7月的第三周到今天——第四周的周二吧。上周周日2...

  • swift第一周学习总结

    swift第一周学习总结 什么是swift语言 swift 语言是苹果公司在2014年的WWDC大会上发布的全新的...

  • 九月第四周

    九月第四周 1.Swift线程锁: https://cloud.tencent.com/developer/art...

  • 第四周Swift总结

    day one 依赖倒转原则(面向协议编程) 声明变量的类型时应该尽可能使用协议类型 声明方法参数类型时应该尽可能...

  • 十月第四周

    十月第四周 1.Swift的map: https://www.cnblogs.com/muzijie/p/6542...

  • 【Be a better girl】系列 6月第四周总结

    【Be a better girl】系列6月第四周总结【6月20日-6月27日】 总结 一、学习【11H】 1、心...

  • 周检视

    周检视【2018年3月第四周】 日期: 从 3月19日 到 3月25日 总结日期: 201803...

  • 六个周的一周总结(09.01.21-09.01.27)

    2019年第四周周总结 09年1月21日 周一 周总结 对第三周的学习进行了一个回顾。 09年1月22日 周二...

网友评论

      本文标题:Swift第四周(第一月)总结

      本文链接:https://www.haomeiwen.com/subject/odcisttx.html