21.5 Objective-C版本的计算器

前面的小节中我们介绍了纯Swift实现的iPhone计算器,这一节我们介绍Objective-C版本的iPhone计算器。由于本书并不是介绍Objective-C语言的书,因此本章我们假定广大读者对Objective-C语言是熟悉的。介绍Objective-C版本的主要目的是比较对于相同的一个应用,使用两种不同的语言(Objective-C和Swift)开发实现有哪些差别。Objective-C版本的iPhone计算器源代码随书一起发布给大家,这里不再详细介绍实现过程。

21.5.1 Xcode工程文件结构比较

我们使用Xcode打开两个工程,Xcode工程文件结构如图21-34所示,左图为Swift版本,右图为Objective-C版本。

{%}

图 21-34 工程文件结构

从图中可见,Objective-C中有.h文件和.m(或.mm)文件,而Swift中只需要一个文件.swift,不需要h文件。至于其他的文件,如资源文件、配置文件和故事板文件(Main.storyboard)都没有区别,甚至在打开故事板Main.storyboard进行界面设计时也都是一样的。

21.5.2 表示层比较

表示层主要是视图控制器ViewController,Objective-C版本中的ViewController.h文件代码如下:

  1. #import <UIKit/UIKit.h>
  2. #import "CalcLogic.h"
  3. @interface ViewController : UIViewController
  4. {
  5. CalcLogic *logic;
  6. }
  7. @property (weak, nonatomic) IBOutlet UILabel *mainLabel;
  8. - (IBAction)operandPressed:(id)sender;
  9. - (IBAction)equalsPressed:(id)sender;
  10. - (IBAction)clearPressed:(id)sender;
  11. - (IBAction)decimalPressed:(id)sender;
  12. - (IBAction)numberButtonPressed:(id)sender;
  13. @end

Objective-C中的h文件用来声明类和类中成员,其中成员变量是在{}之间定义的,代码第①行定义成员变量logic。第②行代码是定义属性,Objective-C属性类似于Swift中的计算属性。第③行代码是声明成员方法,这些方法具体的属性要在m或mm文件中实现。

Objective-C版本中ViewController.m文件代码如下:

  1. #import "ViewController.h"
  2. @interface ViewController ()
  3. @end
  4. @implementation ViewController
  5. - (void)viewDidLoad {
  6. [super viewDidLoad];
  7. self.mainLabel.text = @"0";
  8. logic = [[CalcLogic alloc] init];
  9. }
  10. - (void)didReceiveMemoryWarning {
  11. [super didReceiveMemoryWarning];
  12. }
  13. - (IBAction)operandPressed:(id)sender {
  14. UIButton* btn = (UIButton*)sender;
  15. self.mainLabel.text = [logic calculateByTag:btn.tag withMainLabelString:self.mainLabel.text];
  16. }
  17. - (IBAction)equalsPressed:(id)sender {
  18. UIButton* btn = (UIButton*)sender;
  19. self.mainLabel.text = [logic calculateByTag:btn.tag withMainLabelString:self.mainLabel.text];
  20. }
  21. - (IBAction)clearPressed:(id)sender {
  22. self.mainLabel.text = @"0";
  23. [logic clear];
  24. }
  25. - (IBAction)decimalPressed:(id)sender {
  26. if ( [logic doesStringContainDecimal:self.mainLabel.text] == FALSE) {
  27. NSString s = [self.mainLabel.text stringByAppendingString:@"."];
  28. self.mainLabel.text = s;
  29. }
  30. }
  31. - (IBAction)numberButtonPressed:(id)sender {
  32. UIButton btn = (UIButton*)sender;
  33. self.mainLabel.text = [logic updateMainLabelStringByNumberTag:btn.tag
  34. withMainLabelString:self.mainLabel.text];
  35. }
  36. @end

我们可以比较一下上述代码与Swift中代码的区别,这里不再赘述。

21.5.3 业务逻辑层比较

Objective-C版本中的CalcLogic.h文件代码如下:

  1. #import <Foundation/Foundation.h>
  2. typedef enum {
  3. Plus = 200, Minus, Multiply, Divide,
  4. Default = 0
  5. } Operator;
  6. @interface CalcLogic : NSObject
  7. {
  8. //保存上一次的值
  9. double lastRetainValue;
  10. //最近一次选择的操作符(加、减、乘、除)
  11. Opertor opr;
  12. //临时保存MainLabel内容,为true时,输入数字MainLabel内容被清为0
  13. BOOL isMainLabelTextTemporary;
  14. }
  15. //构造方法
  16. -(id) init;
  17. -(void)clear;
  18. -(NSString*)updateMainLabelStringByNumberTag:(int) tag
  19. withMainLabelString:(NSString*)mainLabelString;
  20. -(BOOL)doesStringContainDecimal:(NSString*)string;
  21. -(NSString*)calculateByTag:(int) tag
  22. withMainLabelString:(NSString*)mainLabelString;
  23. @end

我们在h文件中除了声明CalcLogic类之外,还定义了枚举Operand,代码第①~②行是定义枚举Operand类型,Objective-C中的枚举Operand与Swift枚举有很大的差别。Objective-C中的枚举成员值只能是整数类型。

第③行代码是声明CalcLogic类,它继承了NSObject类,在Objective-C中所有类的根类都是NSObject类。第④行代码是声明构造函数init(),它的作用相当于Swift中的构造器。它的命名一般都是以init开头,返回值是本身类型的指针,而Swift中的构造器没有返回值。

Objective-C版本中的CalcLogic.m文件代码如下:

  1. #import "CalcLogic.h"
  2. @implementation CalcLogic
  3. //构造方法
  4. -(id) init
  5. {
  6. NSLog(@"CalcLogic init");
  7. self = [super init];
  8. if (self) {
  9. lastRetainValue = 0.0;
  10. isMainLabelTextTemporary = FALSE;
  11. opr = Default;
  12. }
  13. return self;
  14. }
  15. -(void)clear
  16. {
  17. lastRetainValue = 0.0;
  18. isMainLabelTextTemporary = FALSE;
  19. opr = Default;
  20. }
  21. -(NSString*)updateMainLabelStringByNumberTag:(int) tag
  22. withMainLabelString:(NSString*)mainLabelString
  23. {
  24. NSString* string = mainLabelString;
  25. if (isMainLabelTextTemporary) {
  26. string = @"0";
  27. isMainLabelTextTemporary = FALSE;
  28. }
  29. int optNumber = tag - 100;
  30. //把String转为double
  31. double mainLabelDouble = string.doubleValue;
  32. if (mainLabelDouble == 0
  33. && [self doesStringContainDecimal: string] == false) {
  34. NSString strOptNumber = [NSString stringWithFormat:@"%i", optNumber];
  35. return strOptNumber;
  36. }
  37. NSString resultString = [string stringByAppendingFormat:@"%i", optNumber];
  38. return resultString;
  39. }
  40. -(BOOL)doesStringContainDecimal:(NSString*)string
  41. {
  42. NSString searchForDecimal = @".";
  43. NSRange range = [string rangeOfString:searchForDecimal];
  44. if (range.location != NSNotFound)
  45. return YES;
  46. return NO;
  47. }
  48. -(NSString)calculateByTag:(int) tag
  49. withMainLabelString:(NSString*)mainLabelString
  50. {
  51. //把String转为为double
  52. double currentValue = mainLabelString.doubleValue;
  53. switch (opr) {
  54. case Plus:
  55. lastRetainValue += currentValue;
  56. break;
  57. case Minus:
  58. lastRetainValue -= currentValue;
  59. break;
  60. case Multiply:
  61. lastRetainValue = currentValue;
  62. break;
  63. case Divide:
  64. if (currentValue != 0) {
  65. lastRetainValue = currentValue;
  66. } else {
  67. opr = Default;
  68. isMainLabelTextTemporary = TRUE ;
  69. return @"错误";
  70. }
  71. break;
  72. Default:
  73. lastRetainValue = currentValue;
  74. }
  75. /记录当前操作符,下次计算时使用
  76. opr = tag;
  77. NSString resultString = [NSString stringWithFormat:@"%g", lastRetainValue];
  78. isMainLabelTextTemporary = TRUE ;
  79. return resultString;
  80. }
  81. @end

上述代码程序结构上与Swift版一样,但在语法上有很大区别,代码第①行self = [super init]语句是先构造父类实例,然后在第②行判断是否成功,如果成功再初始化成员变量。第③行返回自身对象的指针,这就是Objective-C语言的构造过程,要比Swift繁琐。

还有代码第④行使用switch语句。对于switch语句,Objective-C与Swift有很大的差别,Objective-C的switch语句的每一个分支都需要加break,否则就会贯穿;而Swift默认每个分支都有break