今天接着上次的学习笔记往下写,主要是对mvc有个初步的了解,然后今天的代码主要是mvc里的m,即model,所以,我觉得这份笔记需要先看看mvc相关资料再来看,不然可能会有点一头雾水。我觉得可难了,毕竟我不从太聪明,我看了好多资料,把同一节公开课翻来覆去停停顿顿的看了好几遍才有点点感觉……

就趁现在!我要把这份感觉记下来!

首先,要先import一个叫foundation的包,基本每个model都是要import这个包,然后给这个model类起个好听的名字比方说calculatorBrain(我是学老师起的……)

import Foundation

model需要一个东西来储存里面所有的数据,里面的数据有很多种类型,要分开储存才可以在controller问model要数据的时候才可以不会凌乱,这时候,就需要用到enum(枚举)用来分开这些凌乱的数据了。


enum Op: CustomStringConvertible{
case Operand(Double)
case UnaryOperand(String, Double -> Double)
case BinaryOperand(String,(Double,Double) -> Double)
var description:String{
get{
switch self{
case .Operand(let operand):
return "\(operand)"
case .UnaryOperand(let symbol, _):
return symbol
case .BinaryOperand(let symbol, _):
return symbol
}
}
}
}

1.enum是基本类型,可以像class一样有实例,有函数,但是实例只能是计算类实例。
2.一般var数据类型里面都会有get方法和set方法,但是description是只读属性,所以只有get方法,没有set方法。
3.description的作用就是使op转化为一个string返回,由这个属性里的get方法实现。
4.使用自己作为switch的参数,目的是重复使用下面那个evaluate方法里的switch部分代码。

有了这个description属性还不够,还需要实现这个属性,所以要enum op后面implement一个printable,但是在swift2.0中,printable改成了CustomStringConvertale。这个不是继承,不是继承,不是继承,而是protocol,意思是要这个枚举自己实现这个protocol,而恰好这个protocol里面有个description属性可以返回一个string。

涌进来的数据分好之后,就需要一个用于储存数据的栈~
var opStack = [Op]()
有了栈就可以把数据压进栈了:
[php]func pushOperand(operand:Double) -> Double?{
opStack.append(Op.Operand(operand))
return evaluate()
}
[/php]
当一个个数据传进来的时候,需要判断和了解一下在最新一个数据进来之前有没有别的数据,因为有可能进行的是一个长长的运算比方说3*(4+1),所以新建一个字典类型的实例变量来存储已知的运算,每次执行performOperation时会到knowsOps里去查找,当找到之后就把结果传入opStack里
字典类型有两种写法:
第一种:var knownOps = Dictionary()
第二种:var knownOps = [String:Op]()

处理好数据的分类和储存,就要开始动脑子计算了,controller和veiw可不管计算的,model就相当于这个设计里的脑子。因为计算的时候需要用到递归,所以我就弄个递归方法:
[php]func evaluate(ops: [Op]) -> (results: Double?, remainingOps: [Op]){
if !ops.isEmpty{
var remainingOps = ops
let op = remainingOps.removeLast();
switch op {
case .Operand(let operand):
return (operand, remainingOps)
case .UnaryOperand(_,let operation):
//开始递归
let operandEvaluation = evaluate(remainingOps)
//if把let operand从optional double变成了double
if let operand = operandEvaluation.results{
return(operation(operand),operandEvaluation.remainingOps)
}
case .BinaryOperand(_,let operation):
//二次递归
let op1Evaluation = evaluate(remainingOps)
if let operand1 = op1Evaluation.results{
let op2Evaluation = evaluate(op1Evaluation.remainingOps)
if let operand2 = op2Evaluation.results{
return(operation(operand1,operand2),op2Evaluation.remainingOps)
}
}
}
}
return (nil,ops)
}

//递归
func evaluate() -> Double?{
let (result, _) = evaluate(opStack)
return result
}
[/php]
1.第一个递归的返回值很奇怪,没错,返回了一个像结构体一样的东西,在swift里叫tuple,tuple是一种mini数据结构,可以使函数返回多个值,语法简单,就是一对括号里面添加参数,参数之间用逗号隔开,参数名可写可不写。
2.因为递归的时候,想要一个不断变化的变量来储存仍未进行运算的数据,但是ops是不可变的,不可以通过直接改变ops来实现这个想法,所以这里有个老师提供的小 tips:ops是不可变的,但是现在需要一个可变的变量来储存ops,所以用一个var来对ops进行copy,就可以使用removeLast()了,任何变量前面默认都是let,可以创建一个var类型的copy来供本地使用。
3.这里用switch来关联变量取出枚举,这是一个没有default的switch,因为这个swift已经把所有可能的情况都列出来了,不需要default。
4.Operand前的小点是Op.Operand()的省略,swift会自己推测匹配,括号里的operand是个与枚举op里各种情况的关联。
5.处理unaryoperand时,因为不需要知道里面那个string,也就是不需要知道计算符号,只关心计算的函数,所以用下划线来忽略string。swift里“_”=忽略,这样就不用再往后使用这个结构的时候声明其他乱七八糟的变量。
6.如果let后面声明的是一个optional数据类型的变量,可以在let前面加个if就去掉optional了。
7.另一种调用可返回一个tuple值的函数的方法就是直接令一个tuple等于函数返回值,tuple里的参数名不一定要和所调用的函数返回的tuple里的参数名相同,如第二个evaluate函数所示。

上面递归算好了的结果,现在就需要一个东西把结果返回给controller,完成这一次与controller之间的“通话”了:
当CalculatorBrain里面有一定数量的运算的时候,这里就只需要输入运算符号
[php]func performOperation(symbol: String) -> Double?{
if let operation = knownOps[symbol]{
opStack.append(operation)
}
//将每次运算结果返回
return evaluate()
}
}
[/php]

写到这里,这个计算机的model差不多就完成了,因为有了model,所以上节课写的veiwcontroller就需要改变一下,用通过controller与model“通话”的方法替代veiwcontroller的计算操作部分,分工明确一点。
controller和model通过brain来通话连结:
[php]var brain = CalculatorBrain()
@IBAction func operate(sender: UIButton) {
if userIsInTheMiddleOfTypingANumber{
enter()
}
if let operation = sender.currentTitle{
if let result = brain.performOperation(operation){
displayValue = result
}else{
displayValue = 0
}
}
}

@IBAction func enter() {
userIsInTheMiddleOfTypingANumber = false
//每次把operand压进栈,都会通过brain里的evaluate()把运算结果赋值给displayValue
if let result = brain.pushOperand(displayValue){
displayValue = result
}else{
displayValue = 0
}
}
[/php]
1.else里可以试着返回一个错误信息,或者把displayValue清空;如果displayValue是一个optional,这里就可以返回一个错误信息,或者把displayValue清空,总之比直接设为0好啦。

今天就写到这里啦,等我下次文思泉涌的晚上,我再说说我目前对mvc的理解啦,文章里写的有什么不对的不妥的,欢迎斧正哟~

昨天去发廊焗油,洗头的大姐姐说:“小妹妹,你年纪轻轻的脖子怎么那么劳损啊”,我默默的回了一句并且内心在流泪:“大姐,我是学软加工程的😭”,而且最近口腔溃疡两次,让超级无敌馋的我吃东西的时候充分体验到什么叫“痛并快乐着”了😭……