构建第一个Cocoa应用与初始化方法详解
1. 构建TahDoodle应用
在构建TahDoodle应用时,首先要确保滚动视图的大小和位置在窗口变化时能得到保证。当添加足够的约束后,滚动视图的支柱会变为蓝色。
-
操作步骤
:
1. 选择滚动视图,添加约束以保证其大小和位置。
2. 构建并运行应用,此时滚动视图会随窗口大小调整,但窗口变高时可能会覆盖按钮。
3. 为按钮添加自动布局约束,在文档大纲中选择“PushButton”,添加“pin the leading space to the superview”和“pin the bottom space to the superview”两个布局约束。再次构建并运行应用,滚动视图会随窗口调整,按钮会保持在左下角。
2. 建立连接
在InterfaceBuilder中,除了创建和配置视图,还可以将XIB文件中的视图对象连接到应用代码,主要是设置目标 - 动作对和分配指针。
-
File’s Owner
:在BNRDocument.xib的文档大纲中,“File’sOwner”是一个占位符,代表将XIB文件作为用户界面加载的对象,这里代表BNRDocument的实例。
-
设置按钮的目标 - 动作对
:
1. 在布局网格中选择“AddTask”按钮,按住Control键,从按钮拖动到文档大纲中的“File’sOwner”。
2. 释放鼠标按钮,会出现方法列表,选择“addTask:”。此时,BNRDocument成为按钮的目标,“addTask:”成为按钮的动作。
3. 由于在BNRDocument.h中“addTask:”方法声明包含了“IBAction”关键字,所以它才会出现在可选方法列表中。“IBAction”实际上是“void”的宏定义,用于告知InterfaceBuilder在连接目标 - 动作对时包含该方法。
4. 为了运行和测试这个连接,需要在BNRDocument.m中实现“addTask:”方法。
#import "BNRDocument.h"
@implementation BNRDocument
#pragma mark - NSDocument Overrides
...
# pragma mark - Actions
- (void)addTask:(id)sender
{
NSLog(@"Add Task button clicked!");
}
@end
3. 连接表格视图
在BNRDocument.h中声明了“taskTable”指针,需要将BNRDocument.xib中的NSTableView对象分配给该指针。
-
操作步骤
:
1. 重新打开BNRDocument.xib,按住Control键从“File’sOwner”拖动到布局网格中的表格视图,释放鼠标后从连接列表中选择“taskTable”。
2. “Outlet”是“对象指针”的另一种说法,“IBOutlet”在编译前会被移除。
3. 要将BNRDocument实例分配给表格视图的“dataSource”指针,在布局网格中按住Control - Shift键点击表格视图选择NSTableView,然后按住Control键从表格视图拖动到“File’sOwner”,释放鼠标后选择“dataSource”。
4. 实现NSTableViewDataSource
为了让数据源 - 表格视图关系正常工作,需要在BNRDocument.h中声明BNRDocument遵循NSTableViewDataSource协议,并在BNRDocument.m中实现两个必需的方法。
#pragma mark Data Source Methods
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tv
{
return [self.tasks count];
}
- (id)tableView:(NSTableView *)tableView
objectValueForTableColumn:(NSTableColumn *)tableColumn
row:(NSInteger)row
{
return [self.tasks objectAtIndex:row];
}
- (void)tableView:(NSTableView *)tableView
setObjectValue:(id)object
forTableColumn:(NSTableColumn *)tableColumn
row:(NSInteger)row
{
[self.tasks replaceObjectAtIndex:row withObject:object];
[self updateChangeCount:NSChangeDone];
}
同时,更新“addTask:”方法以实际添加任务。
#import "BNRDocument.h"
@implementation BNRDocument
...
#pragma mark - Actions
- (IBAction)addTask:(id)sender
{
NSLog(@"Add Task button clicked!");
if (!self.tasks) {
self.tasks = [NSMutableArray array];
}
[self.tasks addObject:@"New Item"];
[self.taskTable reloadData];
[self updateChangeCount:NSChangeDone];
}
5. 保存和加载数据
为了让应用具备保存和重新打开任务列表的功能,需要重写BNRDocument从NSDocument继承的两个方法。
- (NSData *)dataOfType:(NSString *)typeName
error:(NSError **)outError
{
if (!self.tasks) {
self.tasks = [NSMutableArray array];
}
NSData *data = [NSPropertyListSerialization
dataWithPropertyList:self.tasks
format:NSPropertyListXMLFormat_v1_0
options:0
error:outError];
return data;
}
- (BOOL)readFromData:(NSData *)data
ofType:(NSString *)typeName
error:(NSError **)outError
{
self.tasks = [NSPropertyListSerialization
propertyListWithData:data
options:NSPropertyListMutableContainers
format:NULL
error:outError];
return (self.tasks != nil);
}
6. 何时使用InterfaceBuilder
在简单情况下,使用InterfaceBuilder创建用户界面或通过编程方式设置视图都可行。但一般来说,用户界面越复杂,使用InterfaceBuilder越合适。
7. 编写初始化方法
在NSObject类中,“init”方法用于初始化对象的实例变量。下面以创建“Appliances”项目为例,介绍如何编写初始化方法。
-
创建类
:创建两个类“BNRAppliance”和“BNROwnedAppliance”(BNRAppliance的子类)。在BNRAppliance.h中声明“productName”和“voltage”属性。
#import <Foundation/Foundation.h>
@interface BNRAppliance : NSObject
@property (nonatomic, copy) NSString *productName;
@property (nonatomic) int voltage;
@end
- 基本的init方法 :为了让每个BNRAppliance实例默认电压为120,在BNRAppliance.m中重写“init”方法。
- (instancetype)init
{
self = [super init];
if (self) {
_voltage = 120;
}
return self;
}
- instancetype :初始化方法应返回“instancetype”,它能让编译器期望返回所属类的实例,确保初始化方法可以安全继承。与返回“id”相比,“instancetype”能让编译器检查返回类型。
- 使用和检查超类初始化器 :在“init”方法中,首先调用超类的“init”方法并将返回值赋给“self”,然后检查返回值是否为非空。这是因为有些类的“init”方法可能会有异常情况,如重新分配对象或返回nil。
- 带参数的init方法 :当对象初始化需要额外信息时,需要创建新的初始化方法。对于BNRAppliance,创建“initWithProductName:”方法。
#import <Foundation/Foundation.h>
@interface BNRAppliance : NSObject
@property (nonatomic, copy) NSString *productName;
@property (nonatomic) int voltage;
- (instancetype)initWithProductName:(NSString *)pn;
@end
- (instancetype)initWithProductName:(NSString *)pn
{
self = [super init];
if (self) {
_productName = [pn copy];
_voltage = 120;
}
return self;
}
为了防止其他程序员使用默认的“init”方法创建无效实例,在BNRAppliance.m中重写“init”方法调用“initWithProductName:”并传递默认名称。
- (instancetype)init
{
return [self initWithProductName:@"Unknown"];
}
- 使用访问器 :在初始化方法中,除了直接赋值,也可以使用访问器方法。
- (instancetype)initWithProductName:(NSString *)pn
{
self = [super init];
if (self) {
[self setProductName:pn];
[self setVoltage:120];
}
return self;
}
总结
通过以上步骤,我们完成了TahDoodle应用的构建,包括视图的布局、连接、数据源的实现以及数据的保存和加载。同时,学习了如何编写初始化方法,确保对象在创建时能正确初始化。在实际开发中,根据用户界面的复杂程度选择合适的界面创建方式,并合理使用初始化方法来保证对象的有效性。
流程图
graph TD;
A[开始构建TahDoodle应用] --> B[设置滚动视图约束];
B --> C[设置按钮约束];
C --> D[建立连接(目标 - 动作对、指针分配)];
D --> E[连接表格视图];
E --> F[实现NSTableViewDataSource];
F --> G[保存和加载数据];
H[创建Appliances项目] --> I[创建类(BNRAppliance、BNROwnedAppliance)];
I --> J[编写基本init方法];
J --> K[使用instancetype];
K --> L[使用和检查超类初始化器];
L --> M[编写带参数的init方法];
M --> N[使用访问器];
表格
| 操作内容 | 相关文件 | 关键代码 |
|---|---|---|
| 设置按钮目标 - 动作对 | BNRDocument.h、BNRDocument.m |
- (IBAction)addTask:(id)sender;
|
| 连接表格视图 | BNRDocument.h、BNRDocument.xib |
@property (nonatomic) IBOutlet NSTableView *taskTable;
|
| 实现NSTableViewDataSource | BNRDocument.h、BNRDocument.m |
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tv;
等
|
| 保存和加载数据 | BNRDocument.m |
- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError;
等
|
| 基本init方法 | BNRAppliance.m |
- (instancetype)init;
|
| 带参数的init方法 | BNRAppliance.h、BNRAppliance.m |
- (instancetype)initWithProductName:(NSString *)pn;
|
构建第一个Cocoa应用与初始化方法详解
8. 测试初始化方法
为了测试
BNRAppliance
类的两个初始化方法,需要实现
description
方法,并在
main.m
中进行测试。
// BNRAppliance.m
- (NSString *)description
{
return [NSString stringWithFormat:@"<%@: %d volts>",
self.productName, self.voltage];
}
// main.m
#import <Foundation/Foundation.h>
#import "BNRAppliance.h"
int main (int argc, const char * argv[])
{
@autoreleasepool {
BNRAppliance *a = [[BNRAppliance alloc] init];
NSLog(@"a is %@", a);
[a setProductName:@"Washing Machine"];
[a setVoltage:240];
NSLog(@"a is %@", a);
}
return 0;
}
9. 挑战与拓展
可以为应用添加一个“DeleteSelectedItem”按钮,用于删除当前选中的任务。具体实现步骤如下:
1. 在界面中添加“DeleteSelectedItem”按钮。
2. 为按钮设置目标 - 动作对,目标为
BNRDocument
实例,动作可以命名为
deleteSelectedItem:
。
3. 在
BNRDocument.m
中实现
deleteSelectedItem:
方法,该方法需要获取当前选中的任务并从
tasks
数组中移除,然后刷新表格视图并标记文档为有未保存更改。
// BNRDocument.h
- (IBAction)deleteSelectedItem:(id)sender;
// BNRDocument.m
- (IBAction)deleteSelectedItem:(id)sender
{
NSInteger selectedRow = [self.taskTable selectedRow];
if (selectedRow != -1) {
[self.tasks removeObjectAtIndex:selectedRow];
[self.taskTable reloadData];
[self updateChangeCount:NSChangeDone];
}
}
10. 总结与回顾
在构建Cocoa应用和编写初始化方法的过程中,我们学习了多个重要的知识点:
-
Cocoa应用构建
:通过
InterfaceBuilder
和代码结合的方式,完成了
TahDoodle
应用的界面布局、视图连接、数据源实现以及数据的保存和加载。
-
初始化方法编写
:了解了基本的
init
方法、
instancetype
的使用、超类初始化器的检查、带参数的初始化方法以及访问器在初始化中的应用。
表格
| 知识点 | 关键内容 |
|---|---|
| Cocoa应用构建 | 滚动视图约束、按钮约束、目标 - 动作对设置、表格视图连接、数据源实现、数据保存和加载 |
| 初始化方法编写 |
基本
init
方法、
instancetype
、超类初始化器检查、带参数初始化方法、访问器使用
|
流程图
graph TD;
A[测试初始化方法] --> B[实现description方法];
B --> C[在main.m中测试];
D[挑战拓展] --> E[添加Delete按钮];
E --> F[设置目标 - 动作对];
F --> G[实现deleteSelectedItem:方法];
H[总结回顾] --> I[Cocoa应用构建知识点];
H --> J[初始化方法编写知识点];
总结
通过构建
TahDoodle
应用和编写
Appliances
项目中的初始化方法,我们掌握了Cocoa应用开发的基本流程和Objective - C中初始化方法的编写技巧。在实际开发中,要根据具体需求合理运用这些知识,确保应用的稳定性和可维护性。同时,不断挑战和拓展应用功能,提升自己的开发能力。
超级会员免费看
5343

被折叠的 条评论
为什么被折叠?



