21.3 业务逻辑层开发
表示层依赖于业务逻辑层,因此我们最好先开发业务逻辑层。由于我们采用纯Swift编写应用,因此业务逻辑层也要采用Swift语言编写。
21.3.1 创建CalcLogic.swift文件
首先需要创建一个Swift源代码文件。具体操作方法为:右击工程名,在弹出的快捷菜单中选择“New File…”,然后再打开如图21-10所示的Choose a template for your new file对话框。
图 21-10 选择新创建文件模板
选择iOS→Source→Swif File文件模板,单击“Next”按钮,弹出如图21-11所示的对话框,在Save As中输入文件名“CalcLogic.swift”。在这里我们还可以选择文件保存目录,选择好之后单击“Create”按钮创建文件。如果成功创建文件,就会看到如图21-12所示的界面,一个空的Swift文件就创建好了。
图 21-11 创建文件对话框
图 21-12 创建Swift文件成功
21.3.2 枚举类型Operator
枚举类型Operator
中包含了加、减、乘、除等运算符成员值,用来表示用户所点击的运算符。
Operator
代码如下:
enum Operator : Int {
case Plus = 200, Minus, Multiply, Divide
case Default = 0
}
Operator
枚举类型中定义了5个原始值,均是Int
类型。Plus
表示加运算符,值为200;Minus
表示减运算符,值为201;Multiply
表示乘运算符,值为202;Divide
表示除运算符,值为203;Default
表示默认值,值为0。这些原始值与在Interface Builder设计器中加、减、乘、除运算符按钮的tag
属性值相同,这样的设计便于计算和判断。
Default
成员需要注意,在没有点击任何运算符按钮之前,或者是在用户按了等号按钮计算完成之后,运算符的值都是Default
,因此将Default
的值设为0。
21.3.3 CalcLogic
类中属性
下面我们参考图21-6以及表21-1编写CalcLogic
类中属性。在CalcLogic
类中添加如下代码:
class CalcLogic {
//保存上一次的值
var lastRetainValue : Double
//最近一次选择的操作符(加、减、乘、除)
var opr : Operator
//临时保存MainLabel内容,为true时,输入数字MainLabel内容被清为0
var isMainLabelTextTemporary : Bool
......
}
这几个属性都是存储属性,没有设置默认值,所以我们要在构造器中设置它们的默认值。各个属性的含义已在表21-1中说明了,这里不再赘述。
21.3.4 CalcLogic
类中构造器和析构器
CalcLogic
类中构造器可以初始化存储属性,由于CalcLogic
类中存储属性初始化比较简单,也不需要参数初始化,所以我们设计了无参数构造器init()
,代码如下:
init () {
println("CalcLogic init")
lastRetainValue = 0.0
isMainLabelTextTemporary = false
opr = .Default
}
我们在无参数构造器init()
中初始化了3个存储属性,如果不在构造器中初始化属性,也可以直接在属性声明时初始化。
与构造器对应的是析构器,析构器代码如下:
deinit {
println("CalcLogic deinit")
}
在析构器中也没有什么资源需要释放,因此在本例中我们只是简单地打印输出字符串。
21.3.5 CalcLogic
类中更新主标签方法
CalcLogic
类中定义了更新主标签方法,当用户点击数字按钮后,需要更新主标签内容,表示层给该方法传递的参数是数字按钮的tag
属性和当前主标签内容。
这个过程的计算是比较复杂的:一方面,需要判断当前主标签内容是否是临时的,由于是进行数学计算,当前主标签内容就是计算的结果,当再次输入数字的时候,该数字应该替换主标签内容。另一方面,需要进行小数点的判断,如果已经有小数点了,就不能将小数点追加到主标签之后。在上述两种情况之外,就是正常的将数字追加到主标签之后。
该方法的代码如下:
func updateMainLabelStringByNumberTag(tag : Int,
withMainLabelString mainLabelString : String)->String { ①
var string = mainLabelString ②
if (isMainLabelTextTemporary) { ③
string = “0” ④
isMainLabelTextTemporary = false ⑤
}
let optNumber = tag - 100 ⑥
//把String转为double
var mainLabelDouble = (string as NSString).doubleValue ⑦
if mainLabelDouble == 0 && doesStringContainDecimal(string) == false { ⑧
return String(optNumber) ⑨
}
let resultString = string + String(optNumber) ⑩
return resultString
}
上述代码第①行定义方法,其中第一个参数是tag
,它是按钮的标签属性。第二个参数是mainLabelString
主标签内容,withMainLabelString
是外部参数名。第②行代码是将参数mainLabelString
赋值给字符串变量string
。第③行代码是判断当前主标签内容是否是临时的,如果是临时的,就设置字符串变量string = “0”
,并且设置isMainLabelTextTemporary = false
。
第⑥行代码let optNumber = tag - 100
是通过按钮tag
属性计算操作数,我们在Interface Builder设计器中设置0~9数字按钮的tag
属性是100~109,因此tag -100
计算要进行计算的操作数,如果我们点击的是数字按钮8,它的tag
属性是108,要计算的操作数就是8。
第⑦行代码是将字符串string
转换为Double
类型。字符串string
类型是Swift的String
类型,不能直接转换为Double
类型,需要借助于Foundation框架中的NSString
类进行转换,Foundation框架是用Objective-C语言编写的。在Swift中,只要遵守一定的规范,Swift和Objective-C之间可以互相调用。因此有些功能在Swift中实现很困难,我们可以通过Objective-C的一些类实现该功能,然后再把结果返回给Swift调用程序。
虽然是在Swift中调用Objective-C语言,但是语法还是Swift的语法。第⑦行的(string as NSString).doubleValue
语句是Swift的语法,首先是通过string as NSString
语句将Swift的字符串String
转换为Objective-C的字符串NString
。doubleValue
是NSString
类的实例属性,能够将字符串转换为Double
类型。
上述代码第⑧行是判断主标签内容转换为Double
类型后是否等于0,而且通过doesStringContainDecimal
方法判断是否有小数点。第⑨行代码是将Double
类型转换为String
类型。第⑩行代码是将字符串拼接起来,并返回给调用程序。
21.3.6 CalcLogic
类中判断是否包含小数点方法
CalcLogic
类中定义了判断字符串中是否包含小数点方法,在很多情况下都需要判断一个字符串中是否已经包含了小数点,如果包含则用户不能再输入小数点。总之这个方法是暴露给表示层,至于表示层判断它的目的是什么CalcLogic
类中并不关心。
该方法的代码如下:
func doesStringContainDecimal(string : String)->Bool {
for ch in string {
if ch == "." {
return true
}
}
return false
}
Swift中的字符串String
类没有提供直接判断字符串中是否包含某个特定字符的方法。我们可以使用循环遍历字符串,判断如果有字符为小数点“.
”,则返回true
,否则返回false
。
21.3.7 CalcLogic
类中计算方法
CalcLogic
类中定义了计算方法,该方法的代码如下:
func calculateByTag(tag : Int, withMainLabelString mainLabelString : String)->String { ①
//把String转为为double
var currentValue = (mainLabelString as NSString).doubleValue
switch Operator { ②
case .Plus:
lastRetainValue += currentValue
case .Minus:
lastRetainValue -= currentValue
case .Multiply:
lastRetainValue *= currentValue
case .Divide:
if currentValue != 0 {
lastRetainValue = currentValue
} else {
opr = .Default
isMainLabelTextTemporary = true
return "错误"
}
Default:
lastRetainValue = currentValue ③
}
/记录当前操作符,下次计算时使用
opr = Operator(rawValue:tag)! ④
let resultString = NSString(format: "%g", lastRetainValue) ⑤
isMainLabelTextTemporary = true ⑥
return resultString
}
上述代码第①行是定义方法,其中第一个参数是tag
,它是按钮的标签属性。第二个参数是mainLabelString
主标签内容。
第②行代码是switch
多分支语句,通过switch
语句判断用户点击了哪个运算符,然后进行计算,计算的结果被保存到存储属性lastRetainValue
中。注意当分支为.Divide
(除法运算)时,如果除数为0,计算发生错误,则将字符串“错误”返回。
第③行代码是默认分支,把当前值currentValue
赋值给lastRetainValue
属性。
第④行代码是将枚举的原始值转换为成员值,其中的tag
是按钮tag
属性值。表达式后面的“!
”表示对其进行强力拆封,当tag
无法转换时则把nil
赋值给Operator
。
第⑤行代码是将Double
类型的lastRetainValue
格式化为字符串。格式化过程使用Objective-C的NSString
类协助完成,format:
参数是指定的格式,%g
是用于格式化浮点类型数值。与%f
和%e
不同的是,%g
可以防止出现.00现象,.00现象就是,当输入的是一个整数,例如10,经过格式化后变成了10.00,在计算器应用中是不希望出现.00现象的。
第⑥行代码是给属性isMainLabelTextTemporary
赋值为true
,这是因为计算结束后,用户再输入数字时,原来主标签内容将被清除。通过布尔变量isMainLabelTextTemporary
判断是否已清除主标签内容。
21.3.8 CalcLogic
类中清除方法
当用户点击C(清除)按钮时,需要将CalcLogic
类恢复到初始状态。我们可以通过重新实例化CalcLogic
类来达到这一目的,但是重新实例化会耗费更多系统资源。如果不重新实例化,我们可以编写一个方法类似于构造器初始化存储属性,该方法代码如下:
func clear() {
lastRetainValue = 0.0
isMainLabelTextTemporary = false
opr = .Default
}
该方法所做之事与init
构造器是一样的,都是初始化3个存储属性。表示层通过调用该方法将CalcLogic
类重新初始化。