Categories允许用户为某个类添加新的方法(不能添加新的数据成员),而不用继承的方式来实现。并且,即使没有某个类的实现源码,仍然可以这样做。实例如下:
一. 例1:在不用Fraction源码的情况下,为Fraction添加几个数学(math)方法。
1. 代码:
需要把之前编译好的Fraction.o和Fraction.h放到和下面文件的同一个目录下,例如,这里是FractionMath目录。
//1. FractionMath.h
#import "Fraction.h"
@interface Fraction (Math)
-(Fraction*) add: (Fraction*) f;
-(Fraction*) mul: (Fraction*) f;
-(Fraction*) div: (Fraction*) f;
-(Fraction*) sub: (Fraction*) f;
@end
//2. FractionMath.m
#import "FractionMath.h"
@implementation Fraction (Math)
-(Fraction*) add: (Fraction*) f {
return [[Fraction alloc] initWithNumerator: numerator * [f denominator] +
denominator * [f numerator]
denominator: denominator * [f denominator]];
}
-(Fraction*) mul: (Fraction*) f {
return [[Fraction alloc] initWithNumerator: numerator * [f numerator]
denominator: denominator * [f denominator]];
}
-(Fraction*) div: (Fraction*) f {
return [[Fraction alloc] initWithNumerator: numerator * [f denominator]
denominator: denominator * [f numerator]];
}
-(Fraction*) sub: (Fraction*) f {
return [[Fraction alloc] initWithNumerator: numerator * [f denominator] -
denominator * [f numerator]
denominator: denominator * [f denominator]];
}
@end
//3. main.m
#import <stdio.h>
#import "Fraction.h"
#import "FractionMath.h"
int main( int argc, const char *argv[] ) {
// create a new instance
Fraction *frac1 = [[Fraction alloc] initWithNumerator: 1 denominator: 3];
Fraction *frac2 = [[Fraction alloc] initWithNumerator: 2 denominator: 5];
Fraction *frac3 = [frac1 mul: frac2];
// print it
[frac1 print];
printf( " * " );
[frac2 print];
printf( " = " );
[frac3 print];
printf( "\n" );
// free memory
[frac1 release];
[frac2 release];
[frac3 release];
return 0;
}
2. 编译运行:
gcc -fconstant-string-class=NSConstantString -c main.m -I /GNUstep/System/Library/Headers
gcc -c FractionMath.m -I /GNUstep/System/Library/Headers
gcc main.o Fraction.o FractionMath.o -o main -L /GNUstep/System/Library/Libraries/ -lobjc -lgnustep-base
$ ./main.exe
1/3 * 2/5 = 2/15
3.说明:
(1) 必须将 Fraction.o和Fraction.h放在代码的同一个目录下,这才能引用到,否则,在链接的时候就找不到Fraction类。
二. 例2: 在C++(或Java)那样,对类的方法可以用private/protected/public来限制类方法的访问防属性;然而,在object-c中,却没有这样的规定(有的只是对成员变量的限制), 现在,可以用 categories 来实现这样的功能。实例如下:
1. 代码:
//1. ClassA.h
#import <Foundation/NSObject.h>
@interface ClassA: NSObject
-(void) print;
@end
//2. ClassA.m
#import "ClassA.h"
@implementation ClassA
//在.h中有声明,这是个public方法
-(void) print {
printf( "this is a public method\n");
}
@end
// private 方法
@interface MyClass (Private)
-(void) privateMethod;
@end
@implementation MyClass (Private)
-(void) privateMethod {
printf( "this is a private method\n" );
}
@end
//3. main.m
#import <stdio.h>
#import "ClassA.h"
int main( int argc, const char *argv[] ) {
// create a new instance
ClassA *a = [[ClassA alloc] init];
[a print];
printf( "\n" );
//下面这个函数会编译出错,因为这个函数是在.m中声明的,不能被外部访问,这就间接实现了private功能。
//[a privateMethod];
return 0;
}
二. 编译运行(略)
三. 说明:
(1) 在.h中声明的print函数可以认为是public的;
(2) 在.m中用 "@interface MyClass (Private)"方式声明的,不能被外部访问,所以,间接的实现了private功能。
(3) 当然,可以把"private"改为其它的单词,例如"aaaaaaa",都是可以的;
一. 例1:在不用Fraction源码的情况下,为Fraction添加几个数学(math)方法。
1. 代码:
需要把之前编译好的Fraction.o和Fraction.h放到和下面文件的同一个目录下,例如,这里是FractionMath目录。
//1. FractionMath.h
#import "Fraction.h"
@interface Fraction (Math)
-(Fraction*) add: (Fraction*) f;
-(Fraction*) mul: (Fraction*) f;
-(Fraction*) div: (Fraction*) f;
-(Fraction*) sub: (Fraction*) f;
@end
//2. FractionMath.m
#import "FractionMath.h"
@implementation Fraction (Math)
-(Fraction*) add: (Fraction*) f {
return [[Fraction alloc] initWithNumerator: numerator * [f denominator] +
denominator * [f numerator]
denominator: denominator * [f denominator]];
}
-(Fraction*) mul: (Fraction*) f {
return [[Fraction alloc] initWithNumerator: numerator * [f numerator]
denominator: denominator * [f denominator]];
}
-(Fraction*) div: (Fraction*) f {
return [[Fraction alloc] initWithNumerator: numerator * [f denominator]
denominator: denominator * [f numerator]];
}
-(Fraction*) sub: (Fraction*) f {
return [[Fraction alloc] initWithNumerator: numerator * [f denominator] -
denominator * [f numerator]
denominator: denominator * [f denominator]];
}
@end
//3. main.m
#import <stdio.h>
#import "Fraction.h"
#import "FractionMath.h"
int main( int argc, const char *argv[] ) {
// create a new instance
Fraction *frac1 = [[Fraction alloc] initWithNumerator: 1 denominator: 3];
Fraction *frac2 = [[Fraction alloc] initWithNumerator: 2 denominator: 5];
Fraction *frac3 = [frac1 mul: frac2];
// print it
[frac1 print];
printf( " * " );
[frac2 print];
printf( " = " );
[frac3 print];
printf( "\n" );
// free memory
[frac1 release];
[frac2 release];
[frac3 release];
return 0;
}
2. 编译运行:
gcc -fconstant-string-class=NSConstantString -c main.m -I /GNUstep/System/Library/Headers
gcc -c FractionMath.m -I /GNUstep/System/Library/Headers
gcc main.o Fraction.o FractionMath.o -o main -L /GNUstep/System/Library/Libraries/ -lobjc -lgnustep-base
$ ./main.exe
1/3 * 2/5 = 2/15
3.说明:
(1) 必须将 Fraction.o和Fraction.h放在代码的同一个目录下,这才能引用到,否则,在链接的时候就找不到Fraction类。
二. 例2: 在C++(或Java)那样,对类的方法可以用private/protected/public来限制类方法的访问防属性;然而,在object-c中,却没有这样的规定(有的只是对成员变量的限制), 现在,可以用 categories 来实现这样的功能。实例如下:
1. 代码:
//1. ClassA.h
#import <Foundation/NSObject.h>
@interface ClassA: NSObject
-(void) print;
@end
//2. ClassA.m
#import "ClassA.h"
@implementation ClassA
//在.h中有声明,这是个public方法
-(void) print {
printf( "this is a public method\n");
}
@end
// private 方法
@interface MyClass (Private)
-(void) privateMethod;
@end
@implementation MyClass (Private)
-(void) privateMethod {
printf( "this is a private method\n" );
}
@end
//3. main.m
#import <stdio.h>
#import "ClassA.h"
int main( int argc, const char *argv[] ) {
// create a new instance
ClassA *a = [[ClassA alloc] init];
[a print];
printf( "\n" );
//下面这个函数会编译出错,因为这个函数是在.m中声明的,不能被外部访问,这就间接实现了private功能。
//[a privateMethod];
return 0;
}
二. 编译运行(略)
三. 说明:
(1) 在.h中声明的print函数可以认为是public的;
(2) 在.m中用 "@interface MyClass (Private)"方式声明的,不能被外部访问,所以,间接的实现了private功能。
(3) 当然,可以把"private"改为其它的单词,例如"aaaaaaa",都是可以的;
(4) 对比例一,关键的区别是这个声明是在.h中声明的,还是在.m中声明的,如果是前者,那么,在外部就可以访问,本来的目就是为类扩展函数,正如例1一样; 如果在.m中声明,那么,外部就访问不到了。
(5) 从另一个角度理解,在访问一个类的时候,需要import .h头文件的,如果在.h文件中没有相应函数的声明,那么,就会出现编译错误了,这也很容易理解。