摘要:本文将带你了解IOS开发入门第九章-终章 Swift vs Objective-C,希望本文对大家学IOS有所帮助。
本文将带你了解IOS开发入门第九章-终章 Swift vs Objective-C,希望本文对大家学IOS有所帮助。
到现在为止,你应该对Swift编程语言已经相当熟悉了,是不是忍不住的想要操作练习下?相比有些怪异的Objective-C,我想你也相信强大且简洁的Swift语言一定能取代他了。
在最后一章,你将新建一个应用,这个应用里会涉及很多到目前你所学到的Swift内容,你将创建一个流行的棋牌游戏黑白棋,让你的用户可以和电脑进行比赛。
黑白棋应用已经用Objective-C 实现且发布了,在raywenderlich.com上下两部分文章中有:
? //www.raywenderlich.com/29228/how-to-develop-an-ipad-board- game-app-part-12
//www.raywenderlich.com/29248/how-to-develop-an-ipad-board- game-app-part-22
本章将和Objective-C同样的结构和开发步骤用Swift编写,让你可以同步比较下Swift和Objective-C
在实现上的不同。
Getting started - 开始
在准备的开始项目中已经包含了一些图片等资源。运行项目,会看到如下界面:
花上一些时间去熟悉下项目代码的结构。项目代码里有一个在Main.storyboard中布局的控制器以及一个对应的ViewController.swif代码。控制器有几个连接属性outlets稍后的教程里会介绍。
Modeling the playing board - 建立model棋盘模型
在黑白棋中,玩家不断的在一个8*8的网状格子中替换黑棋和白棋。游戏的规则非常的简单,如果你不熟悉的话可以自行百度。
你的第一个任务是创建一个模型用于表示当前Board棋盘的状态。64个格子中棋子cells可能是黑色,白色或者是空。听起来用Swift的枚举是个不错的选择。
新建一个Swift文件取名叫BoardCellState.swift,并添加到Model文件夹中。添加diam如下:
import Foundation
enum BoardCellState {
case Empty, Black, White
}
上面的枚举定义了棋盘上每个格子上必有的三种状态。
接下来的一系列步骤用于处理棋盘上的逻辑。你将用一对整数:行和列来表示每个方形的格子。将逻辑写入每一个这样的格子中。
在Model文件夹中添加Swift文件BoardLocation.swift。添加内容如下:
import Foundation
struct BoardLocation {
let row: Int, column: Int
init(row: Int, column: Int) {
self.row = row? self.column = column
}
}
上面用一个简单的结构BoardLocation,在其中定义结合了整形的行和列。你可能会想用元组来实现同样的目的,例如let postion:(Int,Int),但是你会发现结构描述的更加清晰些。而且,结构可以实现协议和方法,在本章后续内容中你会使用到这些特征。
现在该处理棋盘board了。在Model文件夹中添加Board.swift,添加内容如下:
import Foundation
class Board {?
private var cells: [BoardCellState]
let boardSize = 8
init () {?
cells = Array(count: boardSize * boardSize, repeatedValue: BoardCellState.Empty)
}
subscript(location: BoardLocation) -> BoardCellState {
get {
return cells[location.row * boardSize + location.column] }
set {?
cells[location.row * boardSize + location.column] = newValue
}
}
}
这定义了一个Board类,包含一个常数数组cells用于表示格子,以及一个常数boardSize表示棋盘大小。初始化创建了个cell数组,里面每个元素都用BoardCellState.Empty进行填充。
subscript提供了一个简明的方法去获取以及设置每个cell的值,比如如下:
var board = Board()
?board[BoardLocation(row: 4, column: 5)] = BoardCellState.White
然而,还有更加简洁的格式,在Board类中添加如下subscript
subscript(row: Int, column: Int) -> BoardCellState {
get {
return self[BoardLocation(row: row, column: column)] }
set {?
self[BoardLocation(row: row, column: column)] = newValue
}
}
上面的subscript你可以如下使用
var board = Board()?
board[4, 5] = BoardCellState.White
Subscripts允许你为你的Swift类创建非常灵活的API。你可以定义更多的subscript,让你能以更多的方式来访问在这类中的数据。
为了测试你刚添加的代码,你需要利用这个model创建一个UI。然而,还有一个更直接立即可用的机制,单元测试!
打开SwiftReversiTests.swift并将以下方法添加到SwiftReversiTests.swift类中。
func test_subscript_setWithValidCoords_cellStateIsChanged() {
let board = Board()
// set the state of one of the cells
board[4, 5] = BoardCellState.White
// verify
let retrievedState = board[4, 5];
XCTAssertEqual(BoardCellState.White, retrievedState, "The cell should have been white!");
}
上面的代码用了个简单的单元测试,实现设置和获取一个board中cell内容。这实现的实际是两个subscript方法。因为第二个获取方法会调用对应的subscript。
在你编译项目前,确保你希望测试的Swift文件在正确的target membership中。你可以打开File Inspector。确保BoardCellState.swift,BoardLocation.swif和Board.swift都同时选中了SwiftReversi和SwiftReversiTests,否则测试代码不会编译。
在xcode菜单栏选择Product\Test 编译并运行tests.会有个清晰的提示告诉你测试代码正在执行
你在控制台内还将看到测试的进展报告以及绿色的小点标记每个测试用例。
注意:本教程的其他部分均没有使用单元测试。继续本教程的学习,你可能会想到使用单元测试来练习。<喎?"/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjwvYmxvY2txdW90ZT4NCjxwPtTaQm9hcmTA4NbQvavDv7jxtcTX+LHq08NzdWJzY3JpcHSxqcK2s/bAtMqut9a438P3o6y1q9Ky09C49tChyLG146GjyOe5+8TjzOG5qbXE0NC78sHQtcTWtbOss/bBy7qvyv21xLHfvefWtaOsseO74dPQ0KnS4s3is/bP1qOsz9bU2r7NwLS+wNX9t8DWudXiuPbOyszioaM8L3A+DQo8cD7U2kJvYXJktcTA4NbQo6zM7bzTyOfPwrXEt723qKO6PC9wPg0KPHByZSBjbGFzcz0="brush:java;">func isWithinBounds(location: BoardLocation) -> Bool { return location.row >= 0 && location.row < boardSize && location.column >= 0 && location.column < boardSize }
上面这个函数简单的确保了BoardLocation的行和列的属性值是有效的。
更新subscript的实现用于检测属性值如下:
subscript(location: BoardLocation) -> BoardCellState {
get {
assert(isWithinBounds(location),?"row or column index out of bounds")
return cells[location.row * boardSize + location.column]
}
set {
assert(isWithinBounds(location), "row or column index out of bounds")
cells[location.row * boardSize + location.column] = newValue
}
}
上面的代码是在subscript的设置和获取的地方设置了断言assertion。当row或column超出边界时,应用将会闪退,如果你在调试模式下的话能捕获到bug的相关信息。
Swift vs. Objective-C
是时候暂停一会儿来比较下Swift和Objective-C在实现黑白棋上不同的特点了。
相对于两个实现的语法有不同之外,还有一些需要注意到的差异。
相对于Objective-C,Swift的subscript提供了一个非常大的进步,当设置cell值的时候需要一个非常长的方法调用
[board setCellState:BoardCellStateWhitePiece forColumn:4 andRow:5];
但Swift的则简单的多:
board[4, 5] = .White?
当需要初始化cell数组时,Objective-C的实现需要用到memset:
memset(_board, 0, sizeof(NSUInteger) * 8 * 8);
你在Swift中真的不应该继续再用c的api。因此,Swift的实现需要另外一种方法,当在初始化数组时过滤值。
cells = Array(count: boardSize * boardSize, repeatedValue: BoardCellState.Empty)
这个函数的代码显然更易读且十分的清晰。
最后,Swift应用只用了个一维数组来保存cells信息,然而Objective-C的实现用了个二维数组。有理由相信这样的性能更好些。在本章的后面会继续了解,和memset相关的内容。
最后一步,你需要添加逻辑去复制游戏棋盘board。Objective-C的实现需要用到c的memcpy函数。你可以很容易的用个2维数组,而Swift的实现只是简单的复制下。幸运的是,在这个应用中,下标subscript会隐藏掉来自于其他类或者子类的实现细节。
Additional game state - 附加游戏的状态
当前的Board类是个通用的,你可以用其来实现游戏的其他扩展。接下来,你需要添加一个Board的子类来实现黑白棋的特殊逻辑。
在Model文件夹中添加Swift文件,并命名为ReversiBoard.swift.添加代码如下:
import Foundation?
class ReversiBoard: Board {
private (set) var blackScore = 0, whiteScore = 0 func
setInitialState() {
super[3,3] = .White
super[4,4] = .White
super[3,4] = .Black
super[4,3] = .Black
blackScore = 2
whiteScore = 2
}
}
ReversiBoard是Board的子类,添加一个简单的setInitialState 用于初始化棋盘在游戏开始时的状态。
打开Board.swift并添加游戏逻辑如下:
func cellVisitor(fn: (BoardLocation) ->()) {
for column in 0..<boardsize boardsize="" column:="" for="" in="" let="" location="BoardLocation(row:" pre="" row=""></boardsize>
上述函数遍历了每一个cell格子,为每一个格子都提供了这个函数。理解这种方法的最好方式就是看他的实际应用。
在同一个文件中,添加一个clearBoard的方法:
func clearBoard() {?
cellVisitor { self[$0] = .Empty }
}
上面的调用了cellVisitor,利用闭包函数来当做fn参数使用。cellVisitor为每一个单元格都调用此函数,$0参数是BoardLocation为每个迭代,clearBoard的结果是让每个cell都设置为Empty。
最后,回到ReversiBoard.swift并在SetInitialState()的顶部调用clearBoard()? 现在在开始的位置为初始化让每一个格子都清理一遍恢复为Empty。
Visualizing the board - 形象化棋盘
开始项目中包含了整个游戏需要的背景图片,然而,你需要去为每个棋格上都创建一个视图,用于包含适当的图片—白色,黑色,或者空。
添加一个Swift文件到View文件夹中,命名为BoardSquare.swift.补充代码如下:
import Foundation import UIKit
class BoardSquare: UIView {?
private let board: ReversiBoard
private let location: BoardLocation
private let blackView: UIImageView
private let whiteView: UIImageView
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
?init(frame: CGRect, location: BoardLocation,
board: ReversiBoard) {
self.board = board
self.location = location
let blackImage = UIImage(named: "ReversiBlackPiece.png")
blackView = UIImageView(image: blackImage) blackView.alpha = 0
let whiteImage = UIImage(named: "ReversiWhitePiece.png")
whiteView = UIImageView(image: whiteImage) whiteView.alpha = 0
&
本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之IOS频道!
您输入的评论内容中包含违禁敏感词
我知道了
请输入正确的手机号码
请输入正确的验证码
您今天的短信下发次数太多了,明天再试试吧!
我们会在第一时间安排职业规划师联系您!
您也可以联系我们的职业规划师咨询:
版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
沪公网安备 31011502005948号