Swift学习 - 第四周知识整理

协议

协议基本定义

  • 协议也能够被继承,但类同时继承父类和协议时,父类必须写在协议前面
  • 协议是方法的集合(计算属性相当于就是方法)
  • 可以把看似不相关的对象的公共行为放到一个协议中
  • 协议在Swift开发中大致有三种作用:
      1. 能力 - 遵循了协议就意味着具备了某种能力
      1. 约定 - 遵循了协议就一定要实现协议中的方法
      1. 角色 - 一个类可以遵循多个协议, 一个协议可以被多个类遵循, 遵循协议就意味着扮演了某种角色, 遵循多个协议就意味着可以扮演多种角色

Swift中的继承是单一继承(一个类只能有一个父类), 如果希望让一个类具备多重能力可以使用协议来实现(C++里面是通过多重继承来实现的, 这是一种非常狗血的做法)

协议扩展

  • 可以在协议扩展中给协议中的方法提供默认实现,也就是说如果某个类遵循了协议但是没有实现这个方法就直接使用默认实现,那么这个方法也就相当于是一个可选方法(可以实现也可以不实现)
// 协议的继承
protocol NiuBi: Flyable, Fightable {
    func dive()
}
// 协议的扩展
protocol Fightable {
    func fight()
}

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

依赖倒转原则(面向协议编程)

  1. 声明变量的类型时应该尽可能使用协议类型
  2. 声明方法参数类型时应该尽可能使用协议类型
  3. 声明方法返回类型时应该尽可能使用协议类型
protocol NiuBi: Flyable, Fightable {
}
class Superman: NiuBi {
}
// 这里的x能接受遵循Flyable, Fightable类型的协议的类,范围较广泛
var x: protocol = Superman()
// 这里的y只能接受遵循Supanman()类型的协议的类
var y: NiuBi = Superman()    

// 协议的组合
let array: [protocol] = [
    Superman(),
]

协议扩充

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

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

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

    1. 协议的设计要小而专不要大而全
    2. 协议的设计也要高度内聚

四人帮设计模式 - 策略模式

/// 图书
class Book {
    var name: String
    var price: Double
    var type: String
    
    // 四人帮设计模式 - 策略模式
    var strategy: DiscountStrategy?
    
    /**
     初始化方法
     - parameter name:  书名
     - parameter price: 价格
     - parameter type:  类型
     */
    init(name: String, price: Double, type: String) {
        self.name = name
        self.price = price
        self.type = type
    }
    
    /// 减多少钱
    var discountValue: Double {
        get {
            if let s = strategy {
                return s.discount(price)
            }
            else {
                return 0
            }
        }
    }
    
    /// 折后价格
    var discountedPrice: Double {
        get { return price - discountValue }
    }
}



/**
 *  打折策略协议
 */
protocol DiscountStrategy { 
    /**
     计算折扣
     - parameter price: 原价
     - returns: 折扣的金额
     */
    func discount(price: Double) -> Double
}
/// 百分比折扣策略
class PercentageDiscount: DiscountStrategy {
    var percentage: Double
    
    init(percentage: Double) {
        self.percentage = percentage
    }
    
    func discount(price: Double) -> Double {
        return price * (1 - percentage)
    }
}
// 固定金额折扣策略
class FixedDiscount: DiscountStrategy {
    var fixedMoney: Double
    
    init(fixedMoney: Double) {
        self.fixedMoney = fixedMoney
    }
    
    func discount(price: Double) -> Double {
        return price >= fixedMoney ? fixedMoney : 0
    }
}
// 分段折后策略
class SegmentedDiscount: DiscountStrategy {
    
    func discount(price: Double) -> Double {
        if price < 20 {
            return 0
        }
        else if price < 50 {
            return 3
        }
        else if price < 100 {
            return 10
        }
        else {
            return 30
        }
    }
}


/// 主方法调用
let booksArray = [
    Book(name: "C语言程序设计", price: 24.0, type: "计算机"),
    Book(name: "名侦探柯南", price: 98.5, type: "漫画"),
    Book(name: "Swift从入门到住院", price: 35.8, type: "计算机"),
    Book(name: "黄冈数学密卷", price: 34.2, type: "教材"),
    Book(name: "中国股市探秘", price: 58.5, type: "金融")
]
let discountDict: [String: DiscountStrategy] = [
    "计算机": PercentageDiscount(percentage: 0.78),
    "教材": PercentageDiscount(percentage: 0.85),
    "漫画": SegmentedDiscount(),
    "科普": FixedDiscount(fixedMoney: 2)
]
var totalPrice = 0.0
var totalDiscount = 0.0
for book in booksArray {
    if let strategy = discountDict[book.type] {
        book.strategy = strategy
    }
    print("《\(book.name)》原价: ¥\(book.price)元")
    print("《\(book.name)》折后价: ¥\(book.discountedPrice)元")
    totalPrice += book.discountedPrice
    totalDiscount += book.discountValue
}
print(String(format: "总计: ¥%.1f元", totalPrice))
print(String(format: "为您节省了: ¥%.1f元", totalDiscount))

我想做燕子

/*
我想做燕子 只需简单思想 只求风中流浪
我想做树 不长六腑五脏 不会寸断肝肠

我做不成燕子 所以我躲不过感情的墙
我做不成树 因此也撑不破伤心的网

来生做燕子吧 随意找棵树休息翅膀 然后淡然飞向远方
来生做树吧 当枝头燕子飞走时 再不去留恋张望
*/

/// 生物
class Creature {
}

// 来生

// 将燕子和树设计成两个协议
protocol Swallow {
    
    func thinkSimply()
    
    func wanderInWind()
}

protocol Tree {
    
    func beHeartless()
}

class Person: Creature {
    var name: String
    
    init(name: String) {
        self.name = name
    }
}

// 小雨康桥除了继承人之外还遵循了燕子和树的协议
class Xiaoyu: Person, Swallow, Tree {
    
    func thinkSimply() {
        print("随意找棵树休息翅膀")
    }
    
    func wanderInWind() {
        print("然后淡然飞向远方")
    }
    
    func beHeartless() {
        print("当枝头燕子飞走时 再不用留恋张望")
    }
}

// 今生

//class Swallow: Creature {
//    
//    func thinkSimply() {
//        print("简单思想")
//    }
//    
//    func wanderInWind() {
//        print("风中流浪")
//    }
//}
//
//class Tree: Creature {
//    
//    func beHeartless() {
//        print("不长六腑五脏")
//        print("不会寸断肝肠")
//    }
//}
//
//class Person: Creature {
//    var name: String
//    
//    init(name: String) {
//        self.name = name
//    }
//}
// 今生的小雨康桥(普通人 变不成燕子和树)
//let xiaoyu: Creature = Person(name: "小雨康桥")

// 来生的小雨康桥(遵循了燕子和树协议的人)
let xiaoyu: Creature = Xiaoyu(name: "小雨康桥")
// 判定小雨康桥可不可以变成燕子
if let swallow = xiaoyu as? Swallow {
    swallow.thinkSimply()
    swallow.wanderInWind()
}
else {
    if let person = xiaoyu as? Person {
        print("\(person.name)躲不过感情的墙")
    }
}
// 判定小雨康桥可不可以变成树
if let tree = xiaoyu as? Tree {
    tree.beHeartless()
}
else {
    if let person = xiaoyu as? Person {
        print("\(person.name)撑不破伤心的网")
    }
}

21点扑克游戏

/**
 花色的枚举
 
 - Spade:   黑桃
 - Heart:   红心
 - Club:    草花
 - Diamond: 方块
 */
enum Suite: String {
    case Spade = "♠️"
    case Heart = "❤️"
    case Club = "♣️"
    case Diamond = "♦️"
}

/// 一张牌
class Card {
    var suite: Suite
    var face: Int
    
    /**
     初始化方法
     */
    init(suite: Suite, face: Int) {
        self.suite = suite
        self.face = face
    }
    
    /// 牌的信息
    var info: String {
        get {
            var str = suite.rawValue
            switch face {
            case 1: str += "A"
            case 11: str += "J"
            case 12: str += "Q"
            case 13: str += "K"
            default: str += "\(face)"
            }
            return str
        }
    }
}



/// 一副牌
class Poker {
    var cardsArray: [Card] = []
    var currentIndex = 0
    
    init() {
        let suitesArray = [Suite.Spade, .Heart, .Club, .Diamond]
        for suite in suitesArray {
            for face in 1...13 {
                let card = Card(suite: suite, face: face)
                cardsArray.append(card)
            }
        }
    }
    
    /**
     洗牌
     */
    func shuffle() {
        currentIndex = 0
        for i in 0.. Card? {
        if hasMoreCards {
            let currentCard = cardsArray[currentIndex]
            currentIndex += 1
            return currentCard
        }
        return nil
    }
    
    /// 还有没有牌
    var hasMoreCards: Bool {
        get { return currentIndex < cardsArray.count }
    }
}



/// 玩家
class Player {
    var name: String
    var money: Int
    var isBanker: Bool
    var cardsOnHand: [Card] = []
    
    /**
     初始化方法
     - parameter name:  姓名
     - parameter money: 金额
     - parameter isBanker: 是不是庄家(默认不是庄家false)
     */
    init(name: String, money: Int, isBanker: Bool = false) {
        self.name = name
        self.money = money
        self.isBanker = isBanker
    }
    
    /**
     得到一张牌
     - parameter card: 牌
     */
    func getOneCard(card: Card) {
        cardsOnHand.append(card)
    }
    
    /**
     弃牌
     */
    func drop() {
        cardsOnHand.removeAll()
    }
    
    /**
     赢
     - parameter bet: 赢得的金额
     */
    func win(bet: Int) {
        money += bet
    }
    
    /**
     输
     - parameter bet: 输掉的金额
     */
    func lose(bet: Int) {
        money -= bet
    }
    
    /// 玩家手上牌的点数
    var totalPoints: Int {
        get {
            // 对玩家手上的牌按点数的降序进行排列(方便计算点数总和)
            let newArray = cardsOnHand.sort { $0.face > $1.face }
            var sum = 0
            for card in newArray {
                switch card.face {
                case 2...9: sum += card.face
                case 10...13: sum += 10
                default: sum += (sum + 11) > 21 ? 1 : 11
                }
            }
            return sum
        }
    }
    
    /// 是否爆炸
    var isBust: Bool {
        get { return totalPoints > 21 }
    }
}



/// 主方法调用
// 显示玩家手上的牌
func showPlayerCards(player: Player, showFirst: Bool = false) {
    print(player.name, terminator: ": ")
    for (index, card) in player.cardsOnHand.enumerate() {
        // 庄家手上的第一张牌只显示背面除非将showFirst设置为true
        if index == 0 && player.isBanker && !showFirst {
            print("", terminator: " ")
        }
        else {
            print(card.info, terminator: "")
        }
    }
    print("")
}

// 根据胜负结算金额
func changeMoney(player: Player, banker: Player, isPlayerWin: Bool, bet: Int) {
    var finalBet = bet
    if isPlayerWin {
        if player.totalPoints == 21 {
            finalBet *= 2
        }
        player.win(finalBet)
        banker.lose(finalBet)
    }
    else {
        player.lose(finalBet)
        banker.win(finalBet)
    }
}

// 创建两个玩家
let player = Player(name: "骆昊", money: 1000)
let banker = Player(name: "阳坚", money: 5000, isBanker: true)
// 创建一副牌
let p = Poker()

repeat {
    print("庄家总资产为: ¥\(banker.money)元")
    print("玩家总资产为: ¥\(player.money)元")

    // 每局开始前玩家和庄家先扔掉手中的牌
    player.drop()
    banker.drop()
    
    // 洗牌
    p.shuffle()

    // 玩家和庄家各获得两张底牌(一明一暗)
    player.getOneCard(p.deal()!)
    player.getOneCard(p.deal()!)
    banker.getOneCard(p.deal()!)
    banker.getOneCard(p.deal()!)

    // 显示玩家和庄家手中的牌
    showPlayerCards(banker)
    showPlayerCards(player)

    // 玩家下注
    var bet: Int
    repeat {
        print("请下注: ", terminator: "")
        bet = inputInt()
    } while bet <= 0 || bet > player.money

    // 选择是否要牌(如果手上的牌已经爆炸了就不能要牌了)
    var choice: Int
    repeat {
        print("是否要牌(1. 要牌; 2. 不要): ", terminator: "")
        choice = inputInt()
        if choice == 1 {
            player.getOneCard(p.deal()!)
            // 每要一张牌就显示一次玩家手上的牌
            showPlayerCards(player)
        }
    } while choice == 1 && !player.isBust

    if player.isBust {  // 玩家爆炸了庄家直接开牌
        print("\(player.name)爆炸了!!!")
        showPlayerCards(banker, showFirst: true)
        changeMoney(player, banker: banker, isPlayerWin: false, bet: bet)
    }
    else {              // 玩家没有爆炸庄家决定是否要牌
        // 产生一个15-20的点数作为庄家要牌的期望值
        let expectPoints = Int(arc4random_uniform(6)) + 15
        // 当庄家手上的牌没有达到期望值也没有爆炸就继续要牌
        while banker.totalPoints < expectPoints && !banker.isBust {
            banker.getOneCard(p.deal()!)
        }
        // 庄家要牌结束后显示庄家手上的牌
        showPlayerCards(banker, showFirst: true)
        if banker.isBust { // 庄家爆了 玩家没爆 玩家胜
            print("\(banker.name)爆炸了!!!")
            changeMoney(player, banker: banker, isPlayerWin: true, bet: bet)
        }
        else {              // 庄家没爆 玩家没爆 比大小
            // 玩家和庄家点数相同庄家胜
            if player.totalPoints > banker.totalPoints {
                print("\(player.name)获得胜利!!!")
                changeMoney(player, banker: banker, isPlayerWin: true, bet: bet)
            }
            else {
                print("\(banker.name)获得胜利!!!")
                changeMoney(player, banker: banker, isPlayerWin: false, bet: bet)
            }
        }
    }
    print("")
} while player.money > 0 && banker.money > 0

// 有一方钱输光了游戏就结束 显示谁获得了胜利
print("庄家总资产为: ¥\(banker.money)元")
print("玩家总资产为: ¥\(player.money)元")
if player.money > 0 {
    print("玩家\(player.name)战胜了庄家\(banker.name)")
}
else {
    print("玩家\(player.name)输给了庄家\(banker.name)")
}

类和结构体的区别

  • 区别1: 结构的对象是值类型, 类的对象是引用类型,值类型在赋值的时候会在内存中进行对象的拷贝,引用类型在赋值的时候不会进行对象拷贝只是增加了一个引用
    结论: 我们自定义新类型时优先考虑使用类而不是结构除非我们要定义的是一种底层的数据结构(保存其他数据的类型)
  • 区别2: 结构会自动生成初始化方法
  • 区别3: 结构中的方法在默认情况下是不允许修改结构中的属性除非加上mutating关键字
var age: Int
mutating func getOlder() {
        age += 1
    }

计算机的硬件由五大部件构成:

  • 运算器、控制器、存储器、输入设备、输出设备
  • 运算器 + 控制器 => CPU (中央处理器)
  • 存储器 => 内存 (RAM - Random Access Memory)

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

  • 栈 (stack) - 我们定义的局部变量/临时变量都是放在栈上
    • 特点: 小、快
  • 堆 (heap) - 我们创建的对象都是放在堆上的
    • 特点: 大、慢
  • 静态区 (static area)
    • 数据段 - 全局量
    • 只读数据段 - 常量
    • 代码段 - 函数和方法

函数的重载

在Swift中同名函数只要参数列表不同是可以共存的 这个叫函数的重载

func changeName(inout name: String) {
    name = "王大锤"
}
// 参数前面加var的做法在Swift 3中肯定是要废掉的
func changeName(var stu: Student2) {
    stu.name = "王大锤"
}

var name = "骆昊"
changeName(&name)
print(name)

var stu = Student2(name: "骆昊", age: 35)
changeName(stu)
print(stu.name)

五子棋游戏

setNeedsDisplay()

  • 刷新画布

委托回调模式

  • 有的时候某个对象要做某件事情但其自身又没有能力做这件事情,这个时候就可以使用委托回调的编程模式让别的对象来做这件事情
  • 实现委托回调的编程模式有以下几个步骤:
    1. 设计一个协议(被委托方必须要遵循协议才能给别的对象当委托)
    2. 委托方添加一个属性其类型是遵循了协议的被委托方
    3. 自己做不了的事情委托给别的对象来做
    4. 如让下例的视图控制器遵循协议成为被委托方(协议表能力)
    5. 遵循协议就必须要实现协议中的方法(协议表约定)
    6. 给下例画布对象绑定委托(self就是视图控制器对象它遵循了协议所以有充当委托的能力也就是说可以扮演被委托方的角色)

guard大法

- guard !renjuBoard.isGameOver else { return }
!renjuBoard.isGameOver条件成立执行下面的代码,否则执行else中代码,return表跳出,不执行以后代码
- Swift 2中的guard大法, Swift 3中据说要废掉

索引器(subscript)语法 - 可以直接对棋盘对象做下标运算来放置棋子

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
            }
        }
    }

if renjuBoard[row, col] {  // renjuBoard是一个棋盘类对象
    renjuBoard[row, col] = renjuBoard.isBlackTurn
    setNeedsDisplay()     // 刷新画布
}   
/**
 棋盘交叉点的状态
 
 - Space: 空格
 - Black: 黑棋
 - White: 白棋
 */
enum PointState {
    case Space, Black, White
}

/// 棋盘
class RenjuBoard {
    var board: [[PointState]]
    var isBlackTurn = true
    var isGameOver = false
    
    init() {
        board = [[PointState]](count: 15, repeatedValue: [PointState](count: 15, repeatedValue: .Space))
    }
    
    // 索引器语法 - 可以直接对棋盘对象做下标运算来放置棋子
    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
            }
        }
    }
    
    func reset() {
        isGameOver = false
        isBlackTurn = true
        for i in 0.. Bool {
        return _judgeH(row, col) || _judgeV(row, col) || _judgeX1(row, col) || _judgeX2(row, col)
    }

    private func _judgeH(row: Int, _ col: Int) -> Bool {
        var counter = 1
        var currentCol = col - 1
        while currentCol >= 0 {
            if board[row][currentCol] == board[row][col] {
                counter += 1
                currentCol -= 1
            }
            else {
                break
            }
        }
        currentCol = col + 1
        while currentCol < board.count {
            if board[row][currentCol] == board[row][col] {
                counter += 1
                currentCol += 1
            }
            else {
                break
            }
        }
        return counter >= 5
    }
    
    private func _judgeV(row: Int, _ col: Int) -> Bool {
        var counter = 1
        var currentRow = row - 1
        while currentRow >= 0 {
            if board[currentRow][col] == board[row][col] {
                counter += 1
                currentRow -= 1
            }
            else {
                break
            }
        }
        currentRow = row + 1
        while currentRow < board.count {
            if board[currentRow][col] == board[row][col] {
                counter += 1
                currentRow += 1
            }
            else {
                break
            }
        }
        return counter >= 5
    }
    
    private func _judgeX1(row: Int, _ col: Int) -> Bool {
        var counter = 1
        var currentRow = row - 1
        var currentCol = col - 1
        while currentRow >= 0 && currentCol > 0 {
            if board[currentRow][currentCol] == board[row][col] {
                counter += 1
                currentRow -= 1
                currentCol -= 1
            }
            else {
                break
            }
        }
        currentRow = row + 1
        currentCol = col + 1
        while currentRow < board.count && currentCol < board.count {
            if board[currentRow][currentCol] == board[row][col] {
                counter += 1
                currentRow += 1
                currentCol += 1
            }
            else {
                break
            }
        }
        return counter >= 5
    }
    
    private func _judgeX2(row: Int, _ col: Int) -> Bool {
        var counter = 1
        var currentRow = row - 1
        var currentCol = col + 1
        while currentRow >= 0 && currentCol < board.count {
            if board[currentRow][currentCol] == board[row][col] {
                counter += 1
                currentRow -= 1
                currentCol += 1
            }
            else {
                break
            }
        }
        currentRow = row + 1
        currentCol = col - 1
        while currentRow < board.count && currentCol >= 0 {
            if board[currentRow][currentCol] == board[row][col] {
                counter += 1
                currentRow += 1
                currentCol -= 1
            }
            else {
                break
            }
        }
        return counter >= 5
    }
    
    func draw() {
        let lineBP = UIBezierPath()
        
        // 绘制15条横线和15条竖线来构造一个棋盘
        for i in 0.., withEvent event: UIEvent?) {
        // Swift 2中的guard大法, Swift 3中据说要废掉
        guard !isAutoMode else { return }
        // guard !renjuBoard.isGameOver else { return }
        
        if !renjuBoard.isGameOver {
            if let touch = touches.first {
                let point = touch.locationInView(self)
                let row = lround(Double(point.y - 10) / 50)
                let col = lround(Double(point.x - 10) / 50)
                if renjuBoard[row, col] {
                    renjuBoard[row, col] = renjuBoard.isBlackTurn
                    setNeedsDisplay()
                    if renjuBoard.judge(row, col) {
                        renjuBoard.isGameOver = true
                        // 3. 自己做不了的事情委托给别的对象来做
                        delegate?.showMessage(self, message: renjuBoard.isBlackTurn ? "白棋胜" : "黑棋胜")
                    }
                }
            }
        }
    }
    
    override func drawRect(rect: CGRect) {
        renjuBoard.draw()
    }

}




//  4. 让视图控制器遵循协议成为被委托方(协议表能力)
class ViewController: UIViewController, CanvasDelegate {
    
    var timer: NSTimer?
    var canvas: Canvas!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        canvas = Canvas(frame: CGRectMake(0, 0, 720, 720))
        // canvas.isAutoMode = true
        //  6. 给画布对象绑定委托(self就是视图控制器对象它遵循了协议所以有充当委托的能力也就是说可以扮演被委托方的角色)
        canvas.delegate = self
        canvas.center = self.view.center
        canvas.backgroundColor = UIColor(red: 254.0 / 255.0, green: 209.0 / 255.0, blue: 46.0 / 255.0, alpha: 1)
        self.view.addSubview(canvas)
        
        // timer = NSTimer.scheduledTimerWithTimeInterval(0.2, target: canvas, selector: "randomMove", userInfo: nil, repeats: true)
    }
    
    //  5. 遵循协议就必须要实现协议中的方法(协议表约定)
    func showMessage(canvas: Canvas, message: String) {
        let alertController = UIAlertController(title: message, message: "", preferredStyle: .Alert)
        let okAction = UIAlertAction(title: "确定", style: .Default) { action in
            // 此处通过尾随闭包来定义点击确定按钮后要做什么
            canvas.clearBoard()
        }
        alertController.addAction(okAction)
        self.presentViewController(alertController, animated: true, completion: nil)
    }
    
    // deinit在销毁对象的时候调用
    deinit {
        // 销毁计时器
        timer?.invalidate()
    }
}

对象引用计数


构造器:

  • 指派构造器(designated)
  • 遍历构造器(convenience)
convenience init() {
        self.init(name: "无名氏", age: 20)
    }
    deinit {
        print("人嗝屁了!")
    }
  • 必要构造器
required init(name: String, age: Int) {
        print("创建一个人!")
        self.name = name
        self.age = age
    }
class GrandFather {
    
}

class Father: GrandFather {
    
}

class Son: Father { 
    override init() {
        // 可以调用Father中的初始化方法
        // 不能调用GrandFather中的初始化方法
    }
}

class Person {
    var name: String
    var age: Int
    
    // 指派构造器前面加上required可以将构造器指定为必要构造器
    // 所谓的必要构造器意味着子类也要提供一模一样的构造器
    // 指派构造器(designated)
    required init(name: String, age: Int) {
        print("创建一个人!")
        self.name = name
        self.age = age
    }
    
    // 便利构造器(convenience)
    convenience init() {
        self.init(name: "无名氏", age: 20)
    }
    
    deinit {
        print("人嗝屁了!")
    }
}

class Student: Person {
    var major: String
    
    required init(name: String, age: Int) {
        major = "未知"
        super.init(name: name, age: age)
    }
    
    convenience init(name: String, age: Int, major: String) {
        // 下面的语句必须写在调用自己的初始化方法之后否则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("老师对象嗝屁了!")
    }
}

//// 创建一个学生对象 然后用stu1去引用它 所以此时学生对象引用计数为1
//var stu1: Student? = Student()
//// 此处没有创建新的学生对象 原来的学生对象的引用计数+1
//var stu2 = stu1
//// 同上 原来的学生对象的引用计数+1
//var stu3 = stu2
//
//// 学生对象引用计数-1
//stu1 = nil
//// 学生对象引用计数-1
//stu2 = nil
//// 学生对象引用计数-1
//// 当学生对象引用计数为0时 ARC会自动清理内存释放学生对象
//// ARC即时性的内存清理 优于Java中的Garbage Collection(垃圾回收)
//stu3 = nil

// var stu1: Student? = Student()
// weak修饰的引用(弱引用)不会增加引用计数 默认是强引用(会增加引用计数)
// weak var stu2 = stu1
// weak var stu3 = stu2

// stu1 = nil
// 如果想释放内存 程序员可以手动将一个引用赋值为nil

// func foo() {
    // stu是一个局部变量/常量 在函数调用结束后局部变量就消失了
    // 所以学生对象的引用计数也就变成0了 所以会被ARC释放掉
    // let stu = Student()
    // print(stu)
// }

// foo()

// 栈 - 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无法释放内存
//class Emp {
//    // 推荐使用
//    // 如果允许使用可空类型通常使用weak来破除循环引用
//    // 如果员工关联的部门对象被释放了那么dept会被赋值为nil
//    // 如果要继续给dept对象发消息程序不会崩溃
//    // weak var dept: Dept?
//    
//    // 谨慎使用
//    // 如果不允许使用可空类型就必须使用unowned来破除循环引用
//    // 需要注意的是如果员工对象关联的部门对象被释放了
//    // 如果还要通过员工对象去操作它所关联的部门对象将导致程序崩溃
//    // EXC_BAD_ACCESS
//    unowned var dept: Dept
//    
//    init(dept: Dept) {
//        print("创建一个员工")
//        self.dept = dept
//    }
//    
//    deinit {
//        print("销毁一个员工")
//    }
//}
//
//class Dept {
//    var manager: Emp?
//    
//    init() {
//        print("创建一个部门")
//    }
//
//    deinit {
//        print("销毁一个部门")
//    }
//}
//
//func bar() {
//    // let person = Person()
//    let dept = Dept()
//    let emp = Emp(dept: dept)
//    dept.manager = emp
//}
//
//bar()

泛型 (generic) - 让类型不再是程序中的硬代码(写死的东西)

func bubbleSort(array: [T]) -> [T] {
    var newArray = array
    for i in 0.. newArray[j + 1] {
                mySwap(&newArray[j], &newArray[j + 1])
                swapped = true
            }
        }
        if !swapped {
            break
        }
    }
    return newArray
}

// 定义一个虚拟类型T, 调用函数时根据传入的参数类型来决定T到底是什么
func mySwap(inout a: T, inout _ b: T) {
    let temp = a
    a = b
    b = temp
}

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


let array1: Array = [23, 45, 99, 12, 68, 51, 70, 66]
let array2 = bubbleSort(array1)
print(array1)
print(array2)

let array3 = ["hello", "zoo", "kiss", "apple", "good"]
let array4 = bubbleSort(array3)
print(array3)
print(array4)

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: "Luo Hao", age: 35)
var stu2 = Student(name: "Wang Dachui", age: 18)

let minStu = myMin(stu1, stu2)
print(minStu.name)

var x = "hello", y = "good"
mySwap(&x, &y)
print(x, y)
print(myMin(x, y))

var a = 3.5, b = 1.2345
mySwap(&a, &b)
print(a, b)
print(myMin(a, b))

var c = 10, d = 100
mySwap(&c, &d)
print(c, d)
print(myMin(c, d))

// Swift中的类、结构和枚举都可以使用泛型
struct Stack {
    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 }
    }
}

var stack = Stack()
stack.push("hello")
stack.push("good")
stack.push("zoo")

while !stack.isEmpty {
    print(stack.pop())
}

分数错误处理等边角知识

// 短除法(欧几里得算法)
// x和y的最大公约数跟y%x和x的最大公约数是一样的
// Greatest Common Divisor
func gcd(x: Int, _ y: Int) -> Int {
    if x > y {
        return gcd(y, x)
    }
    else if y % x != 0 {
        return gcd(y % x, x)
    }
    else {
        return x
    }
}

// 定义一个遵循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)
}






class Person {
    var car: Car?
}

class Car {
    var engine: Engine?
}

class Engine {
    var id: String?
}

let p = Person()
// 可空链语法(适用于开火车式的编程)
print(p.car?.engine?.id?.uppercaseString)

//func <(one: Student, two: Student) -> Bool {
//    return one.name < two.name
//}
//
//let stuArray = [
//    Student(name: "Wang Dachui", age: 24),
//    Student(name: "Lee Xiaolong", age: 49),
//    Student(name: "Zhang Nima", age: 18),
//    Student(name: "Guo Jing", age: 26)
//]
//
//// let newArray = stuArray.sort { $0.age > $1.age }
//let newArray = stuArray.sort(<)
//for stu in newArray {
//    print("\(stu.name): \(stu.age)")
//}

func foo() {
    // 如果能够保证代码不出错可以在try后面加!
    // 如果不确定代码是否出错可以在try后面加?
    // 需要注意的是有?的地方会产生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()


// 对于可能出状况的代码要放在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("出错了! 我也不知道什么问题")
//}

你可能感兴趣的:(Swift学习 - 第四周知识整理)