02 Objective-C

54
Objective-C 范圣刚,[email protected] www.tfan.org

description

Objective-C 基础:类,实例,对象,消息,初始化方法,类方法,数组,字符串和格式化字符串,访问器,异常

Transcript of 02 Objective-C

Page 2: 02 Objective-C

•Objc 基础

•通过⼀一个RandomPossessions 的⼩小程序看⼀一下有关:类,实例,对象,消息,初始化⽅方法,类⽅方法,数组,字符串和格式化字符串,访问器,异常

Page 3: 02 Objective-C

• iOS 应⽤用

•Objective-C: C 语⾔言的扩展

• Cocoa Touch: Objective-C 类的集合

•⾯面向对象编程(object-oriented programming)

Objective-C

Page 4: 02 Objective-C

对象

Page 5: 02 Objective-C

Party的表⽰示•属性•名称•⽇日期•受邀⼈人列表•操作•发送email提醒

•打印名称标签•取消party

Page 6: 02 Objective-C

C

•结构体•数据成员•类型和名称•malloc

• C 函数

Page 7: 02 Objective-C

Class

•类•实例•结构体成员和实例变量•⽅方法:名称,返回值类型,参数列表,实例变量访问•运⾏行⽅方法:message

Page 8: 02 Objective-C

使⽤用实例

Page 9: 02 Objective-C

使⽤用实例•使⽤用类的实例•指向对象的变量•指针变量 -> 对象在内存中的地址

•指向对象的变量声明:Party *partyInstance;

•只是⼀一个可以指向 Party 对象的变量,不是对象本⾝身。

Page 10: 02 Objective-C

创建对象•对象的⽣生命周期:被创建,发送消息,不⽤用的时候销毁

• alloc 消息:Party *partyInstance = [Party alloc];

•当你拥有⼀一个指向实例的指针时,就可以向它发送消息

•第⼀一个消息 -> initialization 消息:[partyInstance init];

•合并:Party *partyInstance = [[Party alloc] init];

• nested message send

Page 11: 02 Objective-C

发送消息

Page 12: 02 Objective-C

消息剖析•中括号中•三个部分

• receiver: ⼀一个指向被要求执⾏行⽅方法的对象的指针

• selector: 要执⾏行的⽅方法的名称

• arguments: 作为参数提供给⽅方法的值

• [partyInstance addAttendee:somePerson]

• 发送 addAttendee: 消息给 partyInstance(receiver) 触发 addAttendee: ⽅方法(通过 selector 命名)并且传⼊入 somePerson(⼀一个argument)

• message 可以有⼀一个参数,多个参数,或者没有参数

Page 13: 02 Objective-C

[ partyInstance addAttendee: somePersonwithDish: deviledEggs ]

receiver 是被发送消息的对象指针

selector 是被触发的⽅方法的名称 arguments 被⽅方法使⽤用

发送消息

Page 14: 02 Objective-C

其他语⾔言partyInstance.addAttendeeWithDish(somePerson, deviledEggs)

Objective-C

[partyInstance addAttendee:somePerson withDish:deviledEggs];

Page 15: 02 Objective-C

销毁对象• partyInstance = nil;

Page 16: 02 Objective-C

nil

• if (venue == nil) ...

• if (!venue) ...

•向 nil 变量发送消息• if (venue) { [venue sendCon!rmation]; }

• nil-check 是没有必要的

Page 17: 02 Objective-C

开始 RandomPossessions

Page 18: 02 Objective-C

把模板代码换成创建和销毁数组实例

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]){

@autoreleasepool { // // insert code here...// NSLog(@"Hello, World!"); // 创建⼀一个 mutable 数组对象,把它的地址保存到 items 变量 NSMutableArray *items = [[NSMutableArray alloc] init]; // 销毁 items 指向的对象 items = nil; } return 0; }

Page 19: 02 Objective-C

•⼀一但实例化完数组以后,我们就可以给它发送消息了,例如 addObject: 和 insertObject:atIndex

int main(int argc, const char * argv[]){

@autoreleasepool { // // insert code here...// NSLog(@"Hello, World!"); // 创建⼀一个 mutable 数组对象,把它的地址保存到 items 变量 NSMutableArray *items = [[NSMutableArray alloc] init]; // 向 items 发送 addObject: 消息,每次传递⼀一个字符串 [items addObject:@"One"]; [items addObject:@"Two"]; [items addObject:@"Three"]; // 发送另外⼀一个消息 insertObject:atIndex 给同样的数组对象 [items insertObject:@"Zero" atIndex:0];

Page 20: 02 Objective-C

•为了确认这些 string 被添加到了数组,我们来加⼀一段代码把它们打印出来

•构建并运⾏行,看⼀一下控制台的输出 // 遍历数组 for (int i=0; i < [items count]; i++) { // 隐式发送 description 消息 NSLog(@"%@", [items objectAtIndex:i]); }

Page 21: 02 Objective-C

创建字符串• [items addObject:@”One”];

•@符号 + 字符串 = Objective-C 中的硬编码字符串

•NSString 的实例

• alloc?

•@:仅针对 NSString 的⽅方便的⽣生成字符串的缩写 NSString *myString = @"Hello, World!"; int len = [myString length]; len = [@"Hello, World" length]; myString = [[NSString alloc] initWithString:@"Hello, World!"]; len = [myString length];

Page 22: 02 Objective-C

格式化字符串•NSLog 打印到控制台,接收可变数量的参数•第⼀一个参数是必须的,⽽而且必须是 NSString 实例,称作 format string,包含⽂文本和⼀一些 token。

• token 以百分号%作为前缀

•每⼀一个传给函数的附加参数替换掉 format string 中的⼀一个token

• token 同时也指定了他们对应的参数的类型 int a = 1; float b = 2.5; char c = 'A'; NSLog(@"整数: %d 浮点数: %f 字符: %c", a, b, c);

Page 23: 02 Objective-C

%@

•任何对象• description 消息,返回⼀一个 NSString

•所有对象都实现了 description 消息

Page 24: 02 Objective-C

NSArray 和 NSMutableArray

•NSArray

•不可变数组•实例化之后就不能再增加或者删除对象了•NSMutableArray

• NSArray ⼦子类

•可变数组•可以被修改(动态增加删除对象)

Page 25: 02 Objective-C

指针,引⽤用和内存地址

Page 26: 02 Objective-C

数组能放什么?•数组只能持有 Objective-C 对象的引⽤用

•基本数据类型和 C 结构体不能增加到数组

•⼀一个单独的数组可以包含不同类型的对象•和强类型语⾔言不同

Page 27: 02 Objective-C

数组操作•⼤大⼩小:int numberOfObjects = [array count];

• addObject: 在数组 后增加对象

• insertObject:atIndex: 在特定位置插⼊入对象

•注意:不能往数组中增加 nil ,可以使⽤用 NSNull

• [array addObject:[NSNull null]]

• objectAtIndex: 获取数据

Page 28: 02 Objective-C

⼦子类化⼀一个 Objective-C 类•层次结构 (hierarchy)

•只有⼀一个超类(superclass)•NSObject

• root class

•⾓角⾊色是实现所有 Cocoa Touch 中的对象的基本⾏行为

• alloc, init, description

Page 29: 02 Objective-C

类的层次结构•扩展•重写

Page 30: 02 Objective-C

创建⼀一个NSObject 的⼦子类• target 勾选• BNRItem, BNRItem.h, BNRItem.m

•保留所有的 C 语⾔言关键字

•Objective-C 额外的关键字使⽤用@前缀区分

•关键字 @interface 在 Objective-C 中声明⼀一个类

•冒号后跟⽗父类•只允许单⼀一继承• @interface ClassName : SuperclassName

• @end 指⽰示类已经被完全声明了

Page 31: 02 Objective-C

实例变量类的实例变量紧跟类的声明之后,在⼤大括号中进⾏行声明

#import <Foundation/Foundation.h>

@interface BNRItem : NSObject{ NSString *itemName; NSString *serialNumber; int valueInDollars; NSDate *dateCreated;}

@end

Page 32: 02 Objective-C

BNRItem 实例

Page 33: 02 Objective-C

访问器⽅方法•有了实例变量之后,要能够 get,或 set 他们的值

• get/set 实例变量的⽅方法称作 accessors, 或者分别叫做getters 和 setters.

•Objective-C 的 setter ⽅方法名:set + 实例变量名

• getter ⽅方法名:和实例变量名⼀一样

Page 34: 02 Objective-C

BNRItem 访问器⽅方法#import <Foundation/Foundation.h>

@interface BNRItem : NSObject{ NSString *itemName; NSString *serialNumber; int valueInDollars; NSDate *dateCreated;}

- (void)setItemName:(NSString *)str;- (NSString *)itemName;

- (void)setSerialNumber:(NSString *)str;- (NSString *)serialNumber;

- (void)setValueInDollars:(int)i;- (int)valueInDollars;

- (NSDate *)dateCreated;

@end

Page 35: 02 Objective-C

实例⽅方法• description

•重写:在实现⽂文件中定义,不需要再做声明// 重写 description- (NSString *)description{ NSString *descriptionString = [[NSString alloc] initWithFormat:@"%@ (%@): 价值 $%d, 登记⽇日期 %@", itemName, serialNumber, valueInDollars, dateCreated]; return descriptionString;}

Page 36: 02 Objective-C

初始化器• alloc -> ⽣生成⼀一个实例,返回⼀一个指向它的指针

• init -> 给所有的实例变量初始化值

•⾃自定义初始化过程简化代码•命名习惯:以init开头

Page 37: 02 Objective-C

BNRItem 的 Initializer

•⽅方法名(或者叫selector)是:initWithItemName:valueInDollars:serialNumber:

•这个selector 具有三个 label:initWithItemName:, valueInDollars:, 和 serialNumber:。表明⽅方法接收三个参数

•这些参数每个都有⼀一个类型和⼀一个参数名•类型在圆括号中,参数名紧跟类型

}- (id)initWithItemName:(NSString *)name valueInDollars:(int)value serialNumber:(NSString *)sNumber;- (void)setItemName:(NSString *)str;

Page 38: 02 Objective-C

id

•返回值类型是id

•指向任意对象的指针• BNRItem * ?

•⼦子类化的问题

Page 39: 02 Objective-C

isa

•从 initializer 返回的对象的类型我们是知道的(向类发送 alloc )

•对象⾃自⾝身也知道它⾃自⼰己的类型 isa 指针

•每个对象都有⼀一个名为 isa 的实例变量

•当⼀一个实例通过向类发送 alloc 创建的时候,类把返回的对象的 isa 实例变量设成指回到⽣生成它的类

• isa 指针的意思就是说这个对象是⼀一个这个类的实例

Page 40: 02 Objective-C

isa 指针

Page 41: 02 Objective-C

实现designated initializer

•第⼀一件事,使⽤用 super 调⽤用超类的初始化器

• 后,使⽤用 self 返回成功初始化过的对象// 实现初始化⽅方法- (id)initWithItemName:(NSString *)name valueInDollars:(int)value serialNumber:(NSString *)sNumber{ // 调⽤用⽗父类的指定的初始化器 self = [super init]; // 给实例变量初始化值 [self setItemName:name]; [self setSerialNumber:sNumber]; [self setValueInDollars:value]; dateCreated = [[NSDate alloc] init]; // 返回初始化对象的地址 return self;}

Page 42: 02 Objective-C

理解 self

•在⽅方法内部,self 是隐式的本地变量

•不需要声明,⾃自动设成指向被发送消息的对象•可以⽤用于给⾃自⾝身发消息的情况下• return self;

Page 43: 02 Objective-C

理解 super

•重写⽅方法时保持超类⽅方法中的操作,在其上增加新的内容

• super:编译器指⽰示符

•指定名称的⽅方法的查找顺序:对象类⾃自⾝身 -> 超类 -> 超类的超类

•给 super 发消息,直接跳过 self 本⾝身, 从其超类开始

• initializer 失败的话返回 nil

• self = [super init];

Page 44: 02 Objective-C

其他初始化器以及初始化链•⼀一个或多个初始化⽅方法•初始化⽅方法链式调⽤用的好处•减少出错的机会•代码更容易维护

// 使⽤用初始化⽅方法链,没有的参数可以传⼊入默认值- (id)initWithItemName:(NSString *)name{ return [self initWithItemName:name valueInDollars:0 serialNumber:@""];}

Page 45: 02 Objective-C

重写 init

- (id)init{ return [self initWithItemName:@"Item" valueInDollars:0 serialNumber:@""];}

Page 46: 02 Objective-C

Initializer 链

Page 47: 02 Objective-C

initializer 简单规则•类从其超类继承了所有的 initializer,同时可以根据其⺫⽬目的任意增加

•每个类选择⼀一个 initializer 作为其 designated initializer

• designated initializer 调⽤用超类的 designated initializer

•类的任意其他 initializer 调⽤用类的 designated initializer

•如果类声明了⼀一个和其超类不同的 designated initializer,超类的 designated initializer 必须被重写来调⽤用新的 designated initializer

Page 48: 02 Objective-C

使⽤用 initializer

•检查重写的 init 是否⽣生效

•使⽤用 designated initializer 替换掉设置实例变量的代码

Page 49: 02 Objective-C

类⽅方法•⽅方法•实例⽅方法•类⽅方法•实例⽅方法被发送给类的实例(像init)

•类⽅方法被发送给类本⾝身(像alloc)

•类⽅方法⼀一般不是被⽤用来创建⼀一个类的新的实例,就是⽤用来提取类的⼀一些全局属性

•类⽅方法不能在实例上操作,或者访问实例变量

Page 50: 02 Objective-C

声明类⽅方法•实例⽅方法的声明: 在返回值前⾯面 使⽤用减号“-” 表⽰示

•类⽅方法的声明:使⽤用加号“+” 字符

• convenience method:stringWithFormat, randomItem, 返回⾃自⾝身类型的对象

•类⽅方法中的 self

•指向类本⾝身,⽽而不是实例•⼦子类调⽤用的问题

Page 51: 02 Objective-C

测试⼦子类

for (int i=0; i<10; i++) { BNRItem *p = [BNRItem randomItem]; [items addObject:p]; } for (int i = 0; i < 10; i++) { NSLog(@"%@", [items objectAtIndex:i]); }

Page 52: 02 Objective-C

异常和⽆无法识别的 Selectors

Page 53: 02 Objective-C

异常•⼀一个对象只会响应它的类实现了相关⽅方法的消息•动态类型的objc,编译时⽆无法确定

•异常exception,run-time errors(运⾏行时错误,和编译时错误相对compile-time errors)

Page 54: 02 Objective-C

快速枚举

•Objective-C 2.0 引⼊入了fast enumeration

for (int i = 0; i < 10; i++) { NSLog(@"%@", [items objectAtIndex:i]); }

for (BNRItem *item in items) { NSLog(@"%@", item); }