代码改变世界

【转】Objective-C学习笔记七:类的定义一

2013-03-10 17:37  maying_07  阅读(155)  评论(0编辑  收藏  举报

原文地址:http://sarin.iteye.com/blog/1761576

我们还是结合之前分数的示例来说明,只是更为详尽。使用XCode创建新的项目Fraction,然后创建Fraction类,此时XCode为我们生成两个文件:Fraction.h和Fraction.m。我们在Fraction.h这个头文件中定义分数类Fraction,在Fraction.m中编写分数类的实现代码,在main.m中编写测试程序。那么在XCode中我们得到如下的项目结构: 

 

    来看下头文件中的类定义:

 

 1 //
 2 //  Fraction.h
 3 //  Fraction
 4 //
 5 //  Created by Nan Lei on 13-1-1.
 6 //  Copyright (c) 2013年 Nan Lei. All rights reserved.
 7 //
 8 
 9 #import <Foundation/Foundation.h>
10 
11 @interface Fraction : NSObject
12 
13 -(void) print;
14 -(void) setNumerator: (int) n;
15 -(void) setDenominator: (int) d;
16 -(int) numerator;
17 -(int) denominator;
18 -(double) convertToNum;
19 
20 @end

 

  这里的头文件也可以叫做接口文件,它会告知编译器Fraction类的特征,就是包含六个实例方法(用"-"定义的方法)。Fraction类的实现会编写在Fraction.m文件中,代码如下: 

 

 1 //
 2 //  Fraction.m
 3 //  Fraction
 4 //
 5 //  Created by Nan Lei on 13-1-1.
 6 //  Copyright (c) 2013年 Nan Lei. All rights reserved.
 7 //
 8 
 9 #import "Fraction.h"
10 
11 @implementation Fraction
12 {
13     int numerator;
14     int denominator;
15 }
16 
17 -(void) print
18 {
19     NSLog(@"%i/%i",numerator,denominator);
20 }
21 
22 -(void) setNumerator:(int)n
23 {
24     numerator=n;
25 }
26 
27 -(void) setDenominator:(int)d
28 {
29     denominator=d;
30 }
31 
32 -(int) numerator
33 {
34     return numerator;
35 }
36 
37 -(int) denominator
38 {
39     return denominator;
40 }
41 
42 -(double) convertToNum
43 {
44     if(denominator!=0){
45         return (double)numerator/denominator;
46     }else{
47         return NAN;
48     }
49 }
50 
51 @end

 

  这和我们之前示例中的是一样的,这里注意一点import语句,我们使用双引号("")来导入头文件,而不是尖括号(<>),原因是Fraction.h是我们自定义的头文件,而不是系统提供的。这样编译器就知道该从什么地方来找到指定的头文件,当然,如果有必要,可以使用更为详细的路径(当我们在不同包下定义了相同的类名的类时)来查找不同位置。 
    最后来看main.m文件,这是我们之前没有做的一个文件,此时我们拆分main函数到main.m文件中,代码如下:

 

 1 //
 2 //  main.m
 3 //  Fraction
 4 //
 5 //  Created by Nan Lei on 13-1-1.
 6 //  Copyright (c) 2013年 Nan Lei. All rights reserved.
 7 //
 8 
 9 #import "Fraction.h"
10 
11 int main(int argc, const char * argv[])
12 {
13     @autoreleasepool {
14         Fraction *fractionA = [Fraction new];
15         Fraction *fractionB = [Fraction new];
16         
17         [fractionA setNumerator:1];
18         [fractionA setDenominator:3];
19         
20         NSLog(@"The value of fractionA is %g",[fractionA convertToNum]);
21         
22         NSLog(@"fractionB is %i / %i",fractionB.numerator,fractionB.denominator);
23         NSLog(@"The value of fractionB is %g",[fractionB convertToNum]);
24     }
25     return 0;
26 }

 

要注意这里的import语句,我们仍然引入的是头文件,而不是实现文件。也就是我们只需获取类定义部分的信息就可以了,系统处理时会为我们寻找实现的方法。 
    这样的代码结构对于之前看到的简单示例来说就复杂多了,这是我们扩大程序规模的必经之路,也更为实用。 
    我们在上述代码中的Fraction实现文件中编写了两个属性numerator和denominator,并且提供了设置值和取值的方法(类似Java的getter和setter方法),其实更好的写法是在接口文件中使用@property指令来定义属性,还可以省略掉设置值和取值的方法(系统会自动为我们实现),那么Fraction.h的代码修改如下: 

 

 1 #import <Foundation/Foundation.h>
 2 
 3 
 4 @interface Fraction : NSObject
 5 
 6 @property int numerator,denominator;
 7 
 8 -(void) print;
 9 -(double) convertToNum;
10 
11 @end

 

 这样一来,代码就简单很多,那么在实现文件Fraction.m中使用@synthesize指令即可完成对属性numerator和denominator的设置值和取值操作: 

 

 1 #import "Fraction.h"
 2 
 3 @implementation Fraction
 4 
 5 @synthesize numerator,denominator;
 6 
 7 -(void) print
 8 {
 9     NSLog(@"%i/%i",numerator,denominator);
10 }
11 
12 -(double) convertToNum
13 {
14     if(denominator!=0){
15         return (double)numerator/denominator;
16     }else{
17         return NAN;
18     }
19 }
20 
21 @end

 

 在XCode中运行代码,我们就得到如下的结果: 

 

    说明我们的代码是正确的。 
    我们使用了@property指令,就不用在实现部分定义相应的实例变量,使用@synthesize指令就会告诉编译器为我们生成设置值和取值的方法。方法命名规范是设置值方法setX(x是我们的变量名),注意大小写(和Java Bean的访问器方法类似),取值方法名就是变量名x。当然我们也可以使用点运算符来访问属性,比如我们代码中的写法。同时使用点运算符来为属性赋值也是可以的,只是在代码中没有体现出来。 
    比如instance.property=value等价于[instance setProperty: value],比如instance.property等价于[instance property]的写法。 
    之前的代码中我们的方法参数都为一个,下面来看看带有多个参数的方法该如何定义和使用。我们继续来改写分数类Fraction,提供一个可以同时设置分子和分母的方法,很容易想到如下语句[fractionA setNumerator:1 setDenominator:3],这是我们很容易想到的方式,但是该方式不具备一个易于理解的方法名,其实我们可以这样来定义这个方法: 

 

1  -(void) setTo: (int) n over: (int) d;

 

下面修改头文件的定义,代码如下: 

 

 1 #import <Foundation/Foundation.h>
 2 
 3 @interface Fraction : NSObject
 4 
 5 @property int numerator,denominator;
 6 
 7 -(void) print;
 8 -(double) convertToNum;
 9 -(void) setTo:(int) n over: (int) d;
10 
11 @end

 

 这里我们加入了这个方法的定义,那么实现该方法,代码如下: 

 

 1 #import "Fraction.h"
 2 
 3 @implementation Fraction
 4 
 5 @synthesize numerator,denominator;
 6 
 7 -(void) print
 8 {
 9     NSLog(@"%i/%i",numerator,denominator);
10 }
11 
12 -(double) convertToNum
13 {
14     if(denominator!=0){
15         return (double)numerator/denominator;
16     }else{
17         return NAN;
18     }
19 }
20 
21 -(void) setTo:(int)n over:(int)d
22 {
23     numerator=n;
24     denominator=d;
25 }
26 
27 @end

 

  最后修改主函数来进行测试: 

 

 1 #import "Fraction.h"
 2 
 3 int main(int argc, const char * argv[])
 4 {
 5     @autoreleasepool {
 6         Fraction *fractionA = [Fraction new];
 7         Fraction *fractionB = [Fraction new];
 8         
 9         [fractionA setTo:10 over:406];
10         [fractionB setTo:23 over:901];
11         
12         [fractionA print];
13         [fractionB print];
14     }
15     return 0;
16 }

 

 

 

 

 

 

 

 

 

 

   编译运行,我们得到如下的结果: 

 

    带有多个参数的方法在定义时,参数名称是可选的,比如上面的方法我们还可以定义为如下形式:-(int) set: (int) n: (int) d;显然这里没有对第二个参数命名,那么调用该方法时,我们可以这样做[fractionA set: 10: 406],虽然可以这么做,但是省略参数名的做法是不推荐的,因为可能会引起语义不清等问题。如果参数很多或者参数很重要,可能就会引起大问题。