6.3 在Xcode中实现MVC
iOS 8开发指南(第2版)
在本书前面的内容中,已经讲解了Xcode及其集成的Interface Builder编辑器的知识。并且在本书上一章的内容中,曾经将故事板场景中的对象连接到了应用程序中的代码。在本节的内容中,将详细讲解将视图绑定到控制器的知识。
6.3.1 视图
在Xcode中,虽然可以使用编程的方式创建视图,但是,在大多数情况下是使用Interface Builder以可视化的方式设计它们。在视图中可以包含众多界面元素,在加载运行阶段程序时,视图可以创建基本的交互对象,例如,当轻按文本框时会打开键盘。要让想视图中的对象能够与应用程序实现逻辑交互,必须定义相应的连接。连接的东西有两种:输出口和操作。输出口定义了代码和视图之间的一条路径,可以用于读写特定类型的信息,例如,对应于开关的输出口让我们能够访问描述开关是开还是关的信息;而操作定义了应用程序中的一个方法,可以通过视图中的事件触发,例如,轻按按钮或在屏幕上轻扫。
如果将输出口和操作连接到代码呢?必须在实现视图逻辑的代码(即控制器)中定义输出口和操作。
6.3.2 视图控制器
控制器在Xcode中被称为视图控制器,功能是负责处理与视图的交互工作,并为输出口和操作之间建立一个人为连接。为此需要在项目代码中使用两个特殊的编译指令:IBAction和IBOutlet。IBAction和IBOutlet是Interface Builder能够识别的标记,它们在Objective-C中没有其他用途。在视图控制器的接口文件中添加这些编译指令。不但可以手工添加,而且也可以用Interface Builder的一项特殊功能自动生成它们。
注意:
视图控制器可包含应用程序逻辑,但这并不意味着所有代码都应包含在视图控制器中。虽然在本书中,大部分代码都放在视图控制器中,当您创建应用程序时,可在合适的时候定义额外的类,以抽象应用程序逻辑。
1.使用IBOutlet
IBOutlet对于编译器来说是一个标记,编译器会忽略这个关键字。Interface Builder则会根据IBOutlet来寻找可以在Builder里操作的成员变量。在此需要注意的是,任何一个被声明为IBOutlet并且在Interface Builder里被连接到一个UI组件的成员变量,会被额外记忆一次,例如:
IBOutlet UILabel *label;
这个label在Interface Builder里被连接到一个UILabel。此时,这个label的retainCount为2。所以,只要使用了IBOutlet变量,一定需要在dealloc或者viewDidUnload中释放这个变量。
IBOutlet的功能是让代码能够与视图中的对象交互。假设在视图中添加了一个文本标签(UILabel),而我们想在视图控制器中创建一个实例“变量/属性”myLabel。此时可以显式地声明它们,也可使用编译指令@property隐式地声明实例变量,并添加相应的属性:
@property (strong, nonatomic) UILabel *myLabel;
这个应用程序提供了一个存储文本标签引用的地方,还提供了一个用于访问它的属性,但还需将其与界面中的标签关联起来。为此,可在属性声明中包含关键字IBOutlet:
@property (strong, nonatomic) IBOutlet UILabel *myLabel;
添加该关键字后,就可以在Interface Builder中以可视化方式将视图中的标签对象连接到变量/属性MyLabel,然后可以在代码中使用该属性与该标签对象交互:修改其文本、调用其方法等。这样,这行代码便声明了实例变量、属性和输出口。
2.使用编译指令property和synthesize简化访问
@property和@synthesize是Objective-C语言中的两个编译指令。实例变量存储的值或对象引用可在类的任何地方使用。如果需要创建并修改一个在所有类方法之间共享的字符串,就应声明一个实例变量来存储它。良好的编程惯例是,不直接操作实例变量。所以要使用实例变量,需要有相应的属性。
编译指令@property定义了一个与实例变量对应的属性,该属性通常与实例变量同名。虽然可以先声明一个实例变量,再定义对应的属性,但是也可以使用@property隐式地声明一个与属性对应的实例变量。例如要声明一个名为myString的实例变量(类型为NSString)和相应的属性,可以编写如下所示的代码实现:
@property (strong, nonatomic) NSString *myString;
这与下面两行代码等效:
NSString *myString;
@property (strong, nonatomic) NSString *myString;
注意:
Apple Xcode工具通常建议隐式地声明实例变量,所以建议大家也这样做。
这同时创建了实例变量和属性,但是,要想使用这个属性则必须先合成它。编译指令@synthesize创建获取函数和设置函数,让我们很容易访问和设置底层实例变量的值。对于接口文件(.h)中的每个编译指令@property,实现文件( .m)中都必须有对应的编译指令@synthesize:
@synthesize myString;
3.使用IBAction
IBAction用于指出在特定的事件发生时应调用代码中相应的方法。假如按下了按钮或更新了文本框,则可能想应用程序采取措施并做出合适的反应。编写实现事件驱动逻辑的方法时,可在头文件中使用IBAction声明它,这将向Interface Builder编辑器暴露该方法。在接口文件中声明方法(实际实现前)被称为创建方法的原型。
例如,方法doCalculation的原型可能类似于下面的情形:
-(IBAction)doCalculation: (id) sender;
注意到该原型包含一个sender参数,其类型为id。这是一种通用类型,当不知道(或不需要知道)要使用的对象的类型时可以使用它。通过使用类型id,可以编写不与特定类相关联的代码,使其适用于不同的情形。创建将用作操作的方法(如doCalculation)时,可以通过参数sender确定调用了操作的对象并与之交互。如果要设计一个处理多种事件(如多个按钮中的任何一个按钮被按下)的方法,这将很方便。