Objective-C:设值/取值方法、参数设置、self关键字、继承、动态绑定、NSObject基本方法、异常处理
Objective-C代码的一般组成:
@interface部分描述类和类的方法(声明)
exp:
@interface Fraction: NSObject -(void) print; -(void) setNumerator: (int)n; -(void) setDenominator: (int)d; @end
声明方法时,-表示为实例方法,+表示为类方法;如果有返回值则将返回类型放入-/+后的括号中;如果有接受参数,则方法名以冒号结束,后接内含参数类型的括号和参数名。
@implementation部分描述数据,并实现接口中声明方法的实际代码(定义)
exp:
@implementation Fraction { int numerator; int denominator; } -(void) print { NSLog (@"%i/%i", numerator, denominator); } -(void) setNumerator:(int)n { numerator = n; } -(void) setDenominator:(int)d { denominator = d; } @end
类名与@interface部分名称相同,指定需要存储的数据类型及名称及实现@implementation中的方法。
@program部分的程序代码实现程序预期目的
int main(int argc, char *argv[]) { @autoreleasepool { Fraction *myFraction; myFraction = [Fraction alloc]; //分配存储空间 myFraction = [myFraction init]; //初始化 //可组合为:Fraction *myFraction = [[Fraction alloc] init]; //也可用new将alloc和init操作结合:Fraction *myFraction = [Fraction new]; [myFraction setNumerator: 1]; [myFraction setDenominator: 3]; NSLog(@"The value of my Fraction is: "); [myFraction print]; } return 0; }
也可在interface和implementation部分为类声明实例变量。
按照约定类名一般以大写字母开头,实例变量、对象及方法一般以小写字母开头。
Id数据类型:一般对象类型,可存储任何类型的对象。
基础数据类型和限定词表
double-%g输出,%lf读入
前缀f、l、u、ll用来明确表示常量是float、long、unsigned和long long类型。
运算中的类型转换
整数变量运算中数字的所有小数部分将丢失,如int a=25, b = 2,a/b=12及int i2=-150, i2/100=-1。
将浮点值赋值给整形变量,所有小数部分将丢失,如float f1=123.125,,int i1=f1,i1=123。
类型转换运算符(如(float))是一元运算符,不会影响变量的值。(int)将浮点值转换为整数,舍去其中的小数部分。
赋值运算符op=优先级低于除逗号运算符外的所有运算符,所以a /= b + c相当于a = a / (b + c)。
去除Xcode时间戳
#ifdef DEBUG #define NSLog(FORMAT, ...) fprintf(stderr,"%s\n",[[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]); #else #define NSLog(...) #endif
优先级:!运算符=一元运算符>二元算术运算符或关系运算符 > &&运算符 > ||运算符>条件与算符>赋值运算符、逗号运算符
条件运算符从右到左结合。
Xcode支持条件运算符的ANSI扩展语句:condition ?: expression
逻辑上等价为condition?condition:expression,对condition求值,若为true则返回值为condition,若为false则返回值为expression。
Objective-C中内置Boolean变量BOOL类型,预定义值YES和NO。
设值方法(setter):设置实例变量值的方法;
取值方法(getter):检索实例变量值的方法;
设值方法和取值方法通常称为访问器(accessor)方法。
Objective-C 2.0提供自动生成设值和取值方法。首先在接口部分(如:Fraction.h)中添加@property指令:
#import <Foundation/Foundation.h> @interface Fraction : NSObject @property int numerator, denominator; -(void) print; -(double) convertToNum; @end
在实现部分(如Fraction.m)中添加@synthesize指令:
#import "Fraction.h" @implementation Fraction { int numerator; int denominator; } @synthesize numerator, denominator; -(void) print { NSLog(@"%i/%i", numerator, denominator); } -(double) convertToNum { if(denominator != 0) return (double)numerator/denominator; else return NAN; } @end
也可不使用@synthesize指令,仅使用@property指令完成,但编译器自动生成的实例变量会以(_)字符作为名称首个字符。
Objective-C支持使用点运算符访问属性。如:
myFraction.numerator 等价于 [myFraction numerator]
instance.property = value 等价于 [instance setProperty: value]
点运算符也支持对自定义的方法使用。
设置具有多个参数的方法,如
[myFraction setNumerator: 1 andDenominator: 3]或[myFraction setTo:1 over:3]
在接口文件Fraction.h添加声明:
-(void) setTo: (int)n over: (int) d;
在实现文件Fraction.m添加方法定义:
-(void) setTo:(int)n over:(int)d { numerator = n; denominator = d; }
在测试程序main.m中声明实例并调用[myFraction setTo:1 over:3] 即可。
也可不对所有参数都设置参数名,如(第二个参数没有命名):
[aFraction set:100 : 200];
在接口文件Fraction.h添加声明:
-(void) set: (int)n : (int) d;
在实现文件Fraction.m添加方法定义:
-(void) set:(int)n :(int)d { numerator = n; denominator = d; }
声明传入参数为该类对象的函数时,应注意参数声明带(*),说明参数是该类对象的一个引用。如,为Fraction类编写add方法:
在接口文件Fraction.h添加声明:
-(void) add: (Fraction *)f;
在实现文件Fraction.m添加方法定义:
-(void) add:(Fraction *)f { numerator = numerator*f.denominator+denominator*f.numerator; denominator = denominator*f.denominator; }
在测试程序main.m中声明实例并调用
[aFraction add:bFraction];
局部静态变量可的声明通常放置在implementation文件开始处(所有方法声明外部),这样所有实例或类方法都可以访问它们。如:
#import “Printer.h” static int pageCount; @implementation Printer … @end
self关键字可用于指明对象是当前方法的接收者,是当前对象的引用。如“在add方法中编写[self reduce]使收到add消息的Fraction对象自动约简。
可创建一个返回值为Fraction的新的add方法。在接口文件Fraction.h添加声明:
-(Fraction *) add: (Fraction *)f;
在实现文件Fraction.m添加方法定义:
-(Fraction *) add:(Fraction *)f { Fraction *result = [[Fraction alloc] init]; result.numerator = numerator*f.denominator+denominator*f.numerator; result.denominator = denominator*f.denominator; [result reduce]; return result; }
在测试程序main.m中声明实例并调用
resultFraction = [aFraction add:bFraction];
resultFraction最终保存了一个Fraction的对象引用,该对象是在add:方法中创建并通过方法返回的。
继承
为了使子类能够直接访问到需要使用的实例变量,需要先在接口部分@interface声明。如果实例变量在实现部分声明和合成(synthesize)则是私有的,子类不能直接访问,需要明确定义或合成取值方法((.)或[])才能访问。
子类继承父类的所有方法和实例变量,类的每个实例拥有自己的实例变量。
在接口部分,如果编译器需要了解另一个自定义的实例变量的类,可以导入头文件(如:#import “XYPoint.h”),也可以使用指令@class XYPoint;使用@class指令提高了效率,因为编译器不需要引入和处理整个XYPoint.h文件,只需要知道XYPoint是一个类名。
在实现部分,@class指令是不够的,因为编译器需要更多的消息。
覆写方法:定义一个同名的新方法,使用和父类相同的名称定义的方法代替或覆写了继承的定义,新方法必须具有相同的返回类型和参数数目。
抽象(abstract)类/抽象超类(abstract superclass):在该类中定义方法和实例变量,目的在于让使用者更容易创建子类,但不期望任何人从这个类创建实例。
多态:使不同的类共享相同方法名称的能力。允许开发一组类,这组类中每一个类都能响应相同的方法名,每个类的定义都封装了响应特定方法所需的代码,使其独立于其他的类定义。多态还允许以后添加新的类,这些新类也能够响应相同的方法名。
将一个变量定义为特定类的对象时使用的是静态类型,静态指对存储在变量中的对象的类型进行显示声明,存储在这种形态中的对象的类是预定义(静态)的。
动态类型绑定:Objective-C系统总是跟踪对象所属的类,先判定对象所属的类,然后在运行时(不是编译时)确定需要动态调用的方法。
id数据类型:一种通用的对象类型,可以用来存储任何类的对象。声明为id对象类型的变量可以用来保存不同类型的对象。
id dataValue; Fraction *f1 = [[Fraction alloc] init]; Complex *c1 = [[Complex alloc] init]; [f1 setTo:2 over:5]; [c1 setReal:10.0 andImaginary:2.5]; dataValue = f1; [dataValue print]; dataValue = c1; [dataValue print];
注意:id对象类型的声明没有使用星号。不能为id变量使用点运算符。
原因:动态类型绑定。在发送消息给id对象时,先检查id对象中存储的对象所属的类,然后在运行时(不是编译时,存储在id变量中的对象类型在编译时无法确定)确定需要动态调用的方法。
使用动态类型调用方法时,需要注意如果在多个类中实现名称相同的方法,每个方法必须符合各个参数的类型和返回值类型,编译器才能为消息表达式生成正确的代码。
NSObject类所支持的一些基本方法
发送class消息可以根据类名或另一个对象生成一个类对象
[Square class]; //从名为Square的类中获得类对象 [mySquare class]; //mySquare是一个实例,查看它所属的类 if ([obj1 class] == [obj2 class]); //查看存储在变量obj1和obj2中的对象是不是相同的类实例 [myFraction isMemberOfClass: [Fraction class]]; 测试变量myFract是不是Fraction类的实例
selector是一个SEL类型的值,可以由一个方法名应用@selector指令生成。如:
@selector (alloc) //为alloc方法生成一个selector,该方法是从NSObject类继承的 @selector (setTo:over:) //为setTo:over:方法生成一个selector,该方法是在Fraction类中实现的(注意方法名称中的冒号字符) [Fraction instancesRespondToSelector: @selector(setTo:over:)] //测试Fraction类的实例是否相应setTo:over方法(包括继承的方法,并不只测试直接定义在类中的方法)
performSelector:方法及其变体允许向对象发送消息,这条消息可以使存储在变量中的selector。如:
SEL action; Id graphicObject; … action = @selector(draw); … [graphicObject performSelector: action]; /*发送SEL变量action所指定的方法到存储在graphicObject中的任何图形对象*/
如果需要先确定对象是否可以响应这个动作:
/*respondsToSelector:判断是否可以将时间的处理委托给你的方法,如果没有实现这个方法,它会按照定义的默认行为自己处理该事件*/ if ([graphicObject respondsToSelector: action] == YES) [graphicObject performSelector: action]; else //错误处理代码
例:
int main(int argc, char *argv[]) { @autoreleasepool { Square *mySquare = [[Square alloc] init]; // isMemberOf:测试类中的直接成员关系 if([mySquare isMemberOfClass:[Square class]] == YES) //yes NSLog(@"mySquare is a member of Square class"); if([mySquare isMemberOfClass:[Rectangle class]] == YES) //no NSLog(@"mySquare is a member of Rectangle class"); if([mySquare isMemberOfClass:[NSObject class]] == YES) //no NSLog(@"mySquare is a member of NSObject class"); // isKindOf:检测继承层次中的关系 if([mySquare isKindOfClass:[Square class]] == YES) //yes NSLog(@"mySqaure is a kind of Square"); if([mySquare isKindOfClass:[Rectangle class]] == YES) //yes NSLog(@"mySquare is a kind of Rectangle"); if([mySquare isKindOfClass:[NSObject class]] == YES) //yes 检测Square类是否响应alloc类方法 NSLog(@"mySquare is a kind of NSObject"); // respondsTo: if([mySquare respondsToSelector:@selector(setSide:)] == YES) //yes NSLog(@"mySquare responds to setSide: method"); if([mySquare respondsToSelector:@selector(setWidth:andHeight:)] == YES) //yes NSLog(@"mySquare responds to setWidth:andHeight: method"); if([Square respondsToSelector:@selector(alloc)] == YES) //yes NSLog(@"Square class responds to alloc method"); // instancesResondTo if([Rectangle instancesRespondToSelector:@selector(setSide:)] == YES) //no NSLog(@"Instances of Rectangle respond to setSide: method"); if([Square instancesRespondToSelector:@selector(setSide:)] == YES) //yes NSLog(@"Instances of Square respond to setSide: method"); if([Square isSubclassOfClass:[Rectangle class]] == YES) //yes NSLog(@"Square is a subclass of a rectangle"); } return 0; }
使用@try处理异常
@try { [f noSuchMethod]; } @catch(NSException *exception) { NSLog(@"Caught %@%@", [exception name], [exception reason]); } NSLog(@"Execution continues!"); }
当@try块抛出异常时,@catch块被执行,包含异常信息的NSException对象作为参数传递给@catch块,name方法检索异常的名称,reason方法给出原因。在@catch块中最后一条语句执行后,程序会立即继续执行之后的语句。
可使用@throw;指令抛出特定的异常。
可使用多个@catch块按顺序捕获并处理各种异常。