協(xié)議
協(xié)議基本定義
- 協(xié)議也能夠被繼承姑尺,但類同時繼承父類和協(xié)議時,父類必須寫在協(xié)議前面
- 協(xié)議是方法的集合(計算屬性相當(dāng)于就是方法)
- 可以把看似不相關(guān)的對象的公共行為放到一個協(xié)議中
- 協(xié)議在Swift開發(fā)中大致有三種作用:
- 能力 - 遵循了協(xié)議就意味著具備了某種能力
- 約定 - 遵循了協(xié)議就一定要實現(xiàn)協(xié)議中的方法
- 角色 - 一個類可以遵循多個協(xié)議, 一個協(xié)議可以被多個類遵循, 遵循協(xié)議就意味著扮演了某種角色, 遵循多個協(xié)議就意味著可以扮演多種角色
Swift中的繼承是單一繼承(一個類只能有一個父類), 如果希望讓一個類具備多重能力可以使用協(xié)議來實現(xiàn)(C++里面是通過多重繼承來實現(xiàn)的, 這是一種非常狗血的做法)
協(xié)議擴展
- 可以在協(xié)議擴展中給協(xié)議中的方法提供默認實現(xiàn)可帽,也就是說如果某個類遵循了協(xié)議但是沒有實現(xiàn)這個方法就直接使用默認實現(xiàn),那么這個方法也就相當(dāng)于是一個可選方法(可以實現(xiàn)也可以不實現(xiàn))
// 協(xié)議的繼承
protocol NiuBi: Flyable, Fightable {
func dive()
}
// 協(xié)議的擴展
protocol Fightable {
func fight()
}
extension Fightable {
func fight() {
print("正在打架")
}
}
依賴倒轉(zhuǎn)原則(面向協(xié)議編程)
- 聲明變量的類型時應(yīng)該盡可能使用協(xié)議類型
- 聲明方法參數(shù)類型時應(yīng)該盡可能使用協(xié)議類型
- 聲明方法返回類型時應(yīng)該盡可能使用協(xié)議類型
protocol NiuBi: Flyable, Fightable {
}
class Superman: NiuBi {
}
// 這里的x能接受遵循Flyable, Fightable類型的協(xié)議的類,范圍較廣泛
var x: protocol<Flyable, Fightable> = Superman()
// 這里的y只能接受遵循Supanman()類型的協(xié)議的類
var y: NiuBi = Superman()
// 協(xié)議的組合
let array: [protocol<Flyable, Fightable>] = [
Superman(),
]
協(xié)議擴充
協(xié)議中全是抽象概念(只有聲明沒有實現(xiàn)) 遵循協(xié)議的類可以各自對協(xié)議中的計算屬性和方法給出自己的實現(xiàn)版本 這樣當(dāng)我們面向協(xié)議編程時就可以把多態(tài)的優(yōu)勢發(fā)揮到淋漓盡致 可以寫出更通用更靈活的代碼(符合開閉原則)
-
實現(xiàn)開閉原則最關(guān)鍵有兩點:
- 抽象是關(guān)鍵(在設(shè)計系統(tǒng)的時候一定要設(shè)計好的協(xié)議);
- 封裝可變性(橋梁模式 - 將不同的可變因素封裝到不同的繼承結(jié)構(gòu)中)
-
接口(協(xié)議)隔離原則:
- 協(xié)議的設(shè)計要小而專不要大而全
- 協(xié)議的設(shè)計也要高度內(nèi)聚
四人幫設(shè)計模式 - 策略模式
/// 圖書
class Book {
var name: String
var price: Double
var type: String
// 四人幫設(shè)計模式 - 策略模式
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 }
}
}
/**
* 打折策略協(xié)議
*/
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
}
}
}
/// 主方法調(diào)用
let booksArray = [
Book(name: "C語言程序設(shè)計", price: 24.0, type: "計算機"),
Book(name: "名偵探柯南", price: 98.5, type: "漫畫"),
Book(name: "Swift從入門到住院", price: 35.8, type: "計算機"),
Book(name: "黃岡數(shù)學(xué)密卷", 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: "為您節(jié)省了: ¥%.1f元", totalDiscount))
我想做燕子
/*
我想做燕子 只需簡單思想 只求風(fēng)中流浪
我想做樹 不長六腑五臟 不會寸斷肝腸
我做不成燕子 所以我躲不過感情的墻
我做不成樹 因此也撐不破傷心的網(wǎng)
來生做燕子吧 隨意找棵樹休息翅膀 然后淡然飛向遠方
來生做樹吧 當(dāng)枝頭燕子飛走時 再不去留戀張望
*/
/// 生物
class Creature {
}
// 來生
// 將燕子和樹設(shè)計成兩個協(xié)議
protocol Swallow {
func thinkSimply()
func wanderInWind()
}
protocol Tree {
func beHeartless()
}
class Person: Creature {
var name: String
init(name: String) {
self.name = name
}
}
// 小雨康橋除了繼承人之外還遵循了燕子和樹的協(xié)議
class Xiaoyu: Person, Swallow, Tree {
func thinkSimply() {
print("隨意找棵樹休息翅膀")
}
func wanderInWind() {
print("然后淡然飛向遠方")
}
func beHeartless() {
print("當(dāng)枝頭燕子飛走時 再不用留戀張望")
}
}
// 今生
//class Swallow: Creature {
//
// func thinkSimply() {
// print("簡單思想")
// }
//
// func wanderInWind() {
// print("風(fēng)中流浪")
// }
//}
//
//class Tree: Creature {
//
// func beHeartless() {
// print("不長六腑五臟")
// print("不會寸斷肝腸")
// }
//}
//
//class Person: Creature {
// var name: String
//
// init(name: String) {
// self.name = name
// }
//}
// 今生的小雨康橋(普通人 變不成燕子和樹)
//let xiaoyu: Creature = Person(name: "小雨康橋")
// 來生的小雨康橋(遵循了燕子和樹協(xié)議的人)
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)撐不破傷心的網(wǎng)")
}
}
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..<cardsArray.count {
let j = Int(arc4random()) % cardsArray.count
(cardsArray[i], cardsArray[j]) = (cardsArray[j], cardsArray[i])
}
}
/**
發(fā)牌
- returns: 返回一張牌或nil
*/
func deal() -> 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: 輸?shù)舻慕痤~
*/
func lose(bet: Int) {
money -= bet
}
/// 玩家手上牌的點數(shù)
var totalPoints: Int {
get {
// 對玩家手上的牌按點數(shù)的降序進行排列(方便計算點數(shù)總和)
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 }
}
}
/// 主方法調(diào)用
// 顯示玩家手上的牌
func showPlayerCards(player: Player, showFirst: Bool = false) {
print(player.name, terminator: ": ")
for (index, card) in player.cardsOnHand.enumerate() {
// 莊家手上的第一張牌只顯示背面除非將showFirst設(shè)置為true
if index == 0 && player.isBanker && !showFirst {
print("??", terminator: " ")
}
else {
print(card.info, terminator: "")
}
}
print("")
}
// 根據(jù)勝負結(jié)算金額
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)
}
}
// 創(chuàng)建兩個玩家
let player = Player(name: "駱昊", money: 1000)
let banker = Player(name: "陽堅", money: 5000, isBanker: true)
// 創(chuàng)建一副牌
let p = Poker()
repeat {
print("莊家總資產(chǎn)為: ¥\(banker.money)元")
print("玩家總資產(chǎn)為: ¥\(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
// 選擇是否要牌(如果手上的牌已經(jīng)爆炸了就不能要牌了)
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 { // 玩家沒有爆炸莊家決定是否要牌
// 產(chǎn)生一個15-20的點數(shù)作為莊家要牌的期望值
let expectPoints = Int(arc4random_uniform(6)) + 15
// 當(dāng)莊家手上的牌沒有達到期望值也沒有爆炸就繼續(xù)要牌
while banker.totalPoints < expectPoints && !banker.isBust {
banker.getOneCard(p.deal()!)
}
// 莊家要牌結(jié)束后顯示莊家手上的牌
showPlayerCards(banker, showFirst: true)
if banker.isBust { // 莊家爆了 玩家沒爆 玩家勝
print("\(banker.name)爆炸了!!!")
changeMoney(player, banker: banker, isPlayerWin: true, bet: bet)
}
else { // 莊家沒爆 玩家沒爆 比大小
// 玩家和莊家點數(shù)相同莊家勝
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
// 有一方錢輸光了游戲就結(jié)束 顯示誰獲得了勝利
print("莊家總資產(chǎn)為: ¥\(banker.money)元")
print("玩家總資產(chǎn)為: ¥\(player.money)元")
if player.money > 0 {
print("玩家\(player.name)戰(zhàn)勝了莊家\(banker.name)")
}
else {
print("玩家\(player.name)輸給了莊家\(banker.name)")
}
類和結(jié)構(gòu)體的區(qū)別
- 區(qū)別1: 結(jié)構(gòu)的對象是值類型, 類的對象是引用類型,值類型在賦值的時候會在內(nèi)存中進行對象的拷貝,引用類型在賦值的時候不會進行對象拷貝只是增加了一個引用
結(jié)論: 我們自定義新類型時優(yōu)先考慮使用類而不是結(jié)構(gòu)除非我們要定義的是一種底層的數(shù)據(jù)結(jié)構(gòu)(保存其他數(shù)據(jù)的類型) - 區(qū)別2: 結(jié)構(gòu)會自動生成初始化方法
- 區(qū)別3: 結(jié)構(gòu)中的方法在默認情況下是不允許修改結(jié)構(gòu)中的屬性除非加上mutating關(guān)鍵字
var age: Int
mutating func getOlder() {
age += 1
}
計算機的硬件由五大部件構(gòu)成:
- 運算器只祠、控制器兜蠕、存儲器扰肌、輸入設(shè)備、輸出設(shè)備
- 運算器 + 控制器 => CPU (中央處理器)
- 存儲器 => 內(nèi)存 (RAM - Random Access Memory)
程序員可以使用的內(nèi)存大致分為五塊區(qū)域:
- 棧 (stack) - 我們定義的局部變量/臨時變量都是放在棧上
- 特點: 小熊杨、快
- 堆 (heap) - 我們創(chuàng)建的對象都是放在堆上的
- 特點: 大曙旭、慢
- 靜態(tài)區(qū) (static area)
- 數(shù)據(jù)段 - 全局量
- 只讀數(shù)據(jù)段 - 常量
- 代碼段 - 函數(shù)和方法
函數(shù)的重載
在Swift中同名函數(shù)只要參數(shù)列表不同是可以共存的 這個叫函數(shù)的重載
func changeName(inout name: String) {
name = "王大錘"
}
// 參數(shù)前面加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()
- 刷新畫布
委托回調(diào)模式
- 有的時候某個對象要做某件事情但其自身又沒有能力做這件事情,這個時候就可以使用委托回調(diào)的編程模式讓別的對象來做這件事情
- 實現(xiàn)委托回調(diào)的編程模式有以下幾個步驟:
- 設(shè)計一個協(xié)議(被委托方必須要遵循協(xié)議才能給別的對象當(dāng)委托)
- 委托方添加一個屬性其類型是遵循了協(xié)議的被委托方
- 自己做不了的事情委托給別的對象來做
- 如讓下例的視圖控制器遵循協(xié)議成為被委托方(協(xié)議表能力)
- 遵循協(xié)議就必須要實現(xiàn)協(xié)議中的方法(協(xié)議表約定)
- 給下例畫布對象綁定委托(self就是視圖控制器對象它遵循了協(xié)議所以有充當(dāng)委托的能力也就是說可以扮演被委托方的角色)
guard大法
- guard !renjuBoard.isGameOver else { return }
!renjuBoard.isGameOver條件成立執(zhí)行下面的代碼晶府,否則執(zhí)行else中代碼桂躏,return表跳出,不執(zhí)行以后代碼
- Swift 2中的guard大法, Swift 3中據(jù)說要廢掉
索引器(subscript)語法 - 可以直接對棋盤對象做下標(biāo)運算來放置棋子
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() // 刷新畫布
}
/**
棋盤交叉點的狀態(tài)
- 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))
}
// 索引器語法 - 可以直接對棋盤對象做下標(biāo)運算來放置棋子
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..<board.count {
for j in 0..<board[i].count {
board[i][j] = .Space
}
}
}
func judge(row: Int, _ col: Int) -> 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條豎線來構(gòu)造一個棋盤
for i in 0..<board.count {
lineBP.moveToPoint(CGPointMake(10, 10 + 50 * CGFloat(i)))
lineBP.addLineToPoint(CGPointMake(710, 10 + 50 * CGFloat(i)))
lineBP.moveToPoint(CGPointMake(10 + 50 * CGFloat(i), 10))
lineBP.addLineToPoint(CGPointMake(10 + 50 * CGFloat(i), 710))
}
lineBP.stroke()
// 繪制棋盤的邊框
let rectBP = UIBezierPath(rect: CGRectMake(3, 3, 714, 714))
rectBP.lineWidth = 6
rectBP.stroke()
// 繪制天元和星
let starsRectArray = [
CGRectMake(155, 155, 10, 10),
CGRectMake(555, 155, 10, 10),
CGRectMake(155, 555, 10, 10),
CGRectMake(555, 555, 10, 10),
CGRectMake(355, 355, 10, 10)
]
for starRect in starsRectArray {
let ovalBP = UIBezierPath(ovalInRect: starRect)
ovalBP.fill()
}
// 繪制棋盤上的棋子
for i in 0..<board.count {
for j in 0..<board[i].count {
if board[i][j] != .Space {
let ovalBP = UIBezierPath(ovalInRect: CGRectMake(-10 + CGFloat(j) * 50, -10 + CGFloat(i) * 50, 40, 40))
(board[i][j] == .Black ? UIColor.blackColor() : UIColor.whiteColor()).set()
ovalBP.fill()
}
}
}
}
}
// 有的時候某個對象要做某件事情但其自身又沒有能力做這件事情
// 這個時候就可以使用委托回調(diào)的編程模式讓別的對象來做這件事情
// 實現(xiàn)委托回調(diào)的編程模式有以下幾個步驟:
// 1. 設(shè)計一個協(xié)議(被委托方必須要遵循協(xié)議才能給別的對象當(dāng)委托)
protocol CanvasDelegate: class {
// 協(xié)議里面的方法就是要委托其他對象做的事情
func showMessage(canvas: Canvas, message: String)
}
class Canvas: UIView {
// 2. 委托方添加一個屬性其類型是遵循了協(xié)議的被委托方
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()
}
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
// Swift 2中的guard大法, Swift 3中據(jù)說要廢掉
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. 讓視圖控制器遵循協(xié)議成為被委托方(協(xié)議表能力)
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就是視圖控制器對象它遵循了協(xié)議所以有充當(dāng)委托的能力也就是說可以扮演被委托方的角色)
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. 遵循協(xié)議就必須要實現(xiàn)協(xié)議中的方法(協(xié)議表約定)
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在銷毀對象的時候調(diào)用
deinit {
// 銷毀計時器
timer?.invalidate()
}
}
對象引用計數(shù)
?
構(gòu)造器:
- 指派構(gòu)造器(designated)
- 遍歷構(gòu)造器(convenience)
convenience init() {
self.init(name: "無名氏", age: 20)
}
deinit {
print("人嗝屁了!")
}
- 必要構(gòu)造器
required init(name: String, age: Int) {
print("創(chuàng)建一個人!")
self.name = name
self.age = age
}
class GrandFather {
}
class Father: GrandFather {
}
class Son: Father {
override init() {
// 可以調(diào)用Father中的初始化方法
// 不能調(diào)用GrandFather中的初始化方法
}
}
class Person {
var name: String
var age: Int
// 指派構(gòu)造器前面加上required可以將構(gòu)造器指定為必要構(gòu)造器
// 所謂的必要構(gòu)造器意味著子類也要提供一模一樣的構(gòu)造器
// 指派構(gòu)造器(designated)
required init(name: String, age: Int) {
print("創(chuàng)建一個人!")
self.name = name
self.age = age
}
// 便利構(gòu)造器(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) {
// 下面的語句必須寫在調(diào)用自己的初始化方法之后否則major屬性會被賦上不正確的值
// self.major = major
self.init(name: name, age: age)
self.major = major
// 初始化的第一階段
// 1. 初始化自己特有的屬性
// self.major = major
// // 子類只能調(diào)用直接父類的構(gòu)造器
// // 子類構(gòu)造器必須調(diào)用父類的非便利構(gòu)造器(指派構(gòu)造器)
// // super.init() // compiler error
// // 2. 調(diào)用父類的初始化方法
// super.init(name: name, age: age)
// // 初始化的第二階段
// // 此處可以調(diào)用對象的方法因為對象已經(jīng)完成了初始化
// study()
}
func study() {
print("\(name)正在學(xué)習(xí).")
}
deinit {
print("學(xué)生對象嗝屁了!")
}
}
class Teacher: Person {
deinit {
print("老師對象嗝屁了!")
}
}
//// 創(chuàng)建一個學(xué)生對象 然后用stu1去引用它 所以此時學(xué)生對象引用計數(shù)為1
//var stu1: Student? = Student()
//// 此處沒有創(chuàng)建新的學(xué)生對象 原來的學(xué)生對象的引用計數(shù)+1
//var stu2 = stu1
//// 同上 原來的學(xué)生對象的引用計數(shù)+1
//var stu3 = stu2
//
//// 學(xué)生對象引用計數(shù)-1
//stu1 = nil
//// 學(xué)生對象引用計數(shù)-1
//stu2 = nil
//// 學(xué)生對象引用計數(shù)-1
//// 當(dāng)學(xué)生對象引用計數(shù)為0時 ARC會自動清理內(nèi)存釋放學(xué)生對象
//// ARC即時性的內(nèi)存清理 優(yōu)于Java中的Garbage Collection(垃圾回收)
//stu3 = nil
// var stu1: Student? = Student()
// weak修飾的引用(弱引用)不會增加引用計數(shù) 默認是強引用(會增加引用計數(shù))
// weak var stu2 = stu1
// weak var stu3 = stu2
// stu1 = nil
// 如果想釋放內(nèi)存 程序員可以手動將一個引用賦值為nil
// func foo() {
// stu是一個局部變量/常量 在函數(shù)調(diào)用結(jié)束后局部變量就消失了
// 所以學(xué)生對象的引用計數(shù)也就變成0了 所以會被ARC釋放掉
// let stu = Student()
// print(stu)
// }
// foo()
// 棧 - FILO 先進后出的結(jié)構(gòu)
// 創(chuàng)建任何子類對象的時候一定是先創(chuàng)建了父類對象
// var stu: Person = Student()
// 引用轉(zhuǎn)移(會導(dǎo)致原來對象上的引用計數(shù)-1 新對象引用計數(shù)+1)
// stu = Teacher()
// stu = Person()
// Swift的自動釋放池
// 通過向autoreleasepool函數(shù)中傳入一個閉包來實現(xiàn)
// autoreleasepool { () -> () in
// 自動釋放池中的對象引用在池的邊界會收到引用計數(shù)-1的消息
// 將來做iOS開發(fā)時如果某個地方會創(chuàng)建很多的臨時對象
// 那么最好在此處設(shè)置一個自動釋放池避免內(nèi)存瞬時峰值過高造成閃退
// let stu1 = Student()
// let stu2 = stu1
// }
// 離開自動釋放池時 stu1會收到引用計數(shù)-1消息 stu2也會收到引用計數(shù)-1消息
// 如果程序中出現(xiàn)了類與類之間雙向關(guān)聯(lián)關(guān)系 必須要將其中一端設(shè)置為weak引用
// 否則將會形成循環(huán)引用導(dǎo)致ARC無法釋放內(nèi)存
//class Emp {
// // 推薦使用
// // 如果允許使用可空類型通常使用weak來破除循環(huán)引用
// // 如果員工關(guān)聯(lián)的部門對象被釋放了那么dept會被賦值為nil
// // 如果要繼續(xù)給dept對象發(fā)消息程序不會崩潰
// // weak var dept: Dept?
//
// // 謹慎使用
// // 如果不允許使用可空類型就必須使用unowned來破除循環(huán)引用
// // 需要注意的是如果員工對象關(guān)聯(lián)的部門對象被釋放了
// // 如果還要通過員工對象去操作它所關(guān)聯(lián)的部門對象將導(dǎo)致程序崩潰
// // EXC_BAD_ACCESS
// unowned var dept: Dept
//
// init(dept: Dept) {
// print("創(chuàng)建一個員工")
// self.dept = dept
// }
//
// deinit {
// print("銷毀一個員工")
// }
//}
//
//class Dept {
// var manager: Emp?
//
// init() {
// print("創(chuàng)建一個部門")
// }
//
// deinit {
// print("銷毀一個部門")
// }
//}
//
//func bar() {
// // let person = Person()
// let dept = Dept()
// let emp = Emp(dept: dept)
// dept.manager = emp
//}
//
//bar()
泛型 (generic) - 讓類型不再是程序中的硬代碼(寫死的東西)
func bubbleSort<T: Comparable>(array: [T]) -> [T] {
var newArray = array
for i in 0..<newArray.count - 1 {
var swapped = false
for j in 0..<newArray.count - 1 - i {
if newArray[j] > newArray[j + 1] {
mySwap(&newArray[j], &newArray[j + 1])
swapped = true
}
}
if !swapped {
break
}
}
return newArray
}
// 定義一個虛擬類型T, 調(diào)用函數(shù)時根據(jù)傳入的參數(shù)類型來決定T到底是什么
func mySwap<T>(inout a: T, inout _ b: T) {
let temp = a
a = b
b = temp
}
// 泛型限定
// <T: Comparable>限定T類型必須是遵循了Comparable協(xié)議的類型
func myMin<T: Comparable>(a: T, _ b: T) -> T {
return a < b ? a : b
}
let array1: Array<Int> = [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中的類川陆、結(jié)構(gòu)和枚舉都可以使用泛型
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 }
}
}
var stack = Stack<String>()
stack.push("hello")
stack.push("good")
stack.push("zoo")
while !stack.isEmpty {
print(stack.pop())
}
分數(shù)錯誤處理等邊角知識
// 短除法(歐幾里得算法)
// x和y的最大公約數(shù)跟y%x和x的最大公約數(shù)是一樣的
// 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協(xié)議的枚舉
// 通過不同的case定義程序中可能出現(xiàn)的若干種異常狀況
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關(guān)鍵字
// throws關(guān)鍵字是提醒方法的調(diào)用者方法可能會出狀況 調(diào)用時要寫try
init(num: Int, den: Int) throws {
_num = num
_den = den
if _den == 0 {
// 如果程序中出現(xiàn)問題就拋出錯誤(異常)
// 被throw關(guān)鍵字拋出的必須是遵循ErrorType協(xié)議的東西
throw FractionError.ZeroDenominator
}
else {
simplify()
normalize()
}
}
func add(other: Fraction) -> Fraction {
// 如果能夠確保方法調(diào)用時不出異常那么可以在try關(guān)鍵字后加!
// 這樣就可以在不寫do...catch的情況下調(diào)用可能出狀況的方法
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后面加?
// 需要注意的是有?的地方會產(chǎn)生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("無效的分數(shù)無法進行加法運算")
}
}
foo()
// 對于可能出狀況的代碼要放在do...catch中執(zhí)行
// 在可能出狀況的方法前還要寫上try表示嘗試著執(zhí)行
// 如果在do中沒有出現(xiàn)任何狀況那么catch就不會執(zhí)行
// 如果do中出現(xiàn)了狀況代碼就不會再向下繼續(xù)執(zhí)行而是轉(zhuǎn)移到catch中
// 在do的后面可以跟上多個catch用于捕獲不同的異常狀況 但是最多只有一個catch會被執(zhí)行
//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("出錯了! 我也不知道什么問題")
//}