获取一个类中定义的所有方法
如果你想获取一个类中定义的所有方法的数组,不包含任何它的超类,你可以使用class_copyMethodList()函数。这个函数返回一个类的方法实现的数组,声明如下:[/color] [color=#231f20] Method * class_copyMethodList(Class cls, unsigned int *outCount)
第一个参数是类对象,第二个是一个整数的引用。如果你想获得实例方法,你通过类对象。如果,另一方面,你想获得所有方法的列表,你需要通过元对象。这个作为类(需要获得它的方法)的蓝图这个有道理,它是元类对象。返回的数组包括方法类型的指针。当你完成它的时候你需要释放返回的数组。[/color] [color=#231f20] 让我们通过一个例子说明。考虑下面的类:[/color] [color=#231f20] @interface RT5 :NSObject +(void)simpleS; +(NSString*)complexS:(NSNumber*)aNumber andAString:(NSString*)aString; -(void)simple; -(NSString*)complex:(NSNumber*)aNumber andAString:(NSString*)aString; @end
@implementation RT5 +(void)simpleS{} +(NSString*)complexS:(NSNumber*)aNumber andAString:(NSString*)aString{ return @"Hi"; } -(void)simple{} -(NSString*)complex:(NSNumber*)aNumber andAString:(NSString*)aString{ return @"Hello"; } @end
列出类方法,我们调用到我们listAllMethods()方法,如下:[/color] [color=#231f20] listAllMethods(object_getClass([RT5 class]));
在这里,我们通过类RT5的元类作为唯一的参数。我们在类对象中通过运行函数object_getClass()获得元类对象。[/color] [color=#231f20] 列出所有实例方法,我们通过如下类对象:[/color] [color=#231f20] listAllMethods([RT5 class]);
The listAllMethods()function is shown below: listAllMethods()函数,如下所示:[/color] [color=#231f20] void listAllMethods(Classclass){ unsigned int methodCount; Method *methods = class_copyMethodList(class, &methodCount); for(int i=0; i < methodCount; i++){ char buffer[256]; SEL name = method_getName(methods); NSLog(@"The method’s name is %@", NSStringFromSelector(name)); //OR //NSLog(@"The method’s name is %s", name); char *returnType = method_copyReturnType(methods); NSLog(@"The return type is %s", returnType); free(returnType); // self, _cmd + any others unsigned int numberOfArguments= method_getNumberOfArguments(methods); for(int j=0; j<numberOfArguments;j++){ method_getArgumentType(methods, j, buffer, 256); NSLog(@"The type of argument %d is %s", j, buffer); } } free(methods); }
上述的函数,获得类对象的方法数组通过一个参数开始。获得方法数组后,该方法遍历这个数组记录每个方法的具体属性。上述的方法记录的方法的名称,返回类型,每个属性类型。[/color] [color=#231f20] 获得返回的方法类型,你可以使用method_copyReturnType()函数声明入戏:[/color] [color=#231f20] char *method_copyReturnType(Method m)
获得指定参数的类型,你可以使用method_getArgumentType()函数,声明如下:[/color] [color=#231f20] void method_getArgumentType(Method m, unsigned int index,char *dst, size_t dst_len)
你通过作为方法对象的第一个参数。其指定类型是作为第二个参数传递。一个指向存储返回类型的缓冲区的指针通过作为第三个参数。最后,缓冲区的大小通过作为第四个参数。[/color] [color=#231f20] 例如,这里是输出相关类的方法之一:[/color] [color=#231f20] 方法的名称是 complexS:andAString: 返回的类型是 @ 参数0的类型是@ 参数1的类型是:[/color] [color=#231f20]参数2的类型是@ 参数3的类型是@
注意,前两个参数是每个方法的隐藏函数。[/color] [color=#231f20]
添加一个新方法[/color] [color=#231f20] 如果你想到一个类里添加一个新方法,你可以使用函数class_addMethod(),声明如下:[/color] [color=#231f20] BOOL class_addMethod(Class cls, SEL name, IMP imp, constchar *types)
首先前三个参数你应该很熟悉了。最后一个参数,是你提供编码返回和参数类型的C字符串。这个方法名必须不能被类cls实现。如果它是超类cls之一声明,它会被重写。[/color] [color=#231f20] 让我们通过一个例子说明。考虑一个类RT6没有定义方法。假设我们想添加一个新的实例方法,selector newOne:withData:签名如下:[/color] [color=#231f20] -(NSString*) newOne:(BOOL)flagwithData:(NSData*)data;
C函数实现这个新方法,如下所示:[/color] [color=#231f20] NSString* newOneImplementation(id self,SEL _cmd,BOOLflag,NSData* data){ NSLog(@"self: %@, _cmd: %@", self, NSStringFromSelector(_cmd)); NSLog(@"The flag is: %@ and the data is: %@",flag?@"YES":@"NO", data); return @"Done!"; }
添加一个新方法,我们简单的调用class_addMethod()函数,如下所示:[/color] [color=#231f20] class_addMethod([RT6 class], @selector(newOne:withData:), (IMP)newOneImplementation,"@@:B@");
我们通过类对象,因为它是我们添加的一个实例方法。方法名是通过第二个参数。一个指向我们实现的指针,是通过第三个参数。第四个参数是一个集中编码的C字符串。我们知道,每一个方法的前两个参数是编码为@的对象,和selector编码为:。NSString*返回类型和它的编码为@。我们方法的第一个和第二个参数是BOOL(编码作为B)和NSData*编码为@。注意类型通常列在第一个位。[/color] [color=#231f20] 一旦我们添加了一个新方法,我们作为任何Objective-C方法调用它。你将,然而,获得一个编译警告。Objective-C编译环境是聪明的,但它不是那么聪明!
RT6 *t = [[[RT6 alloc] init] autorelease]; NSLog(@"%@", [t newOne:YES withData:[NSDatadata]]);
添加一个类方法,通过object_getClass([RT6class])作为class_addMethod()函数的第一个参数。[/color] [color=#231f20]
发送消息到一个对象[/color] [color=#231f20] 你已经看到了我们的朋友objc_msgSend()函数。如果你发现需要使用此功能,你可以在你的地方使用它。例如,考虑下面的类:[/color] [color=#231f20] @interface RT7 : NSObject +(NSUInteger) lengthS:(NSString*)theString; -(NSUInteger) length:(NSString*)theString; @end
@implementation RT7 +(NSUInteger) lengthS:(NSString*)theString{ return [theString length]; } -(NSUInteger) length:(NSString*)theString{ return [theString length]; } @end
调用实例方法length:,你可以写成下面的:[/color] [color=#231f20] RT7 *rt = [[[RT7 alloc] init] autorelease]; NSUInteger value; value = ((int(*)(id, SEL, NSString*))objc_msgSend)( rt, @selector(length:), @"hi");
构造objc_msgSend()函数是可选的。它不仅能一处编译器警告。[/color] [color=#231f20] 调用类方法lengthS:,你可以写成[/color] [color=#231f20] value = ((int(*)(id, SEL, NSString*))objc_msgSend)( [RT7 class], @selector(lengthS:), @"hello");
访问实例变量[/color] [color=#231f20] 你可以直接访问对象的实例变量。访问一个实例变量,使用object_getInstanceVariable()函数,声明如下[/color] [color=#231f20] Ivar object_getInstanceVariable(idobj, const char *name, void **outValue);
返回Ivar类型的值,实例变量运行期间的类型。这个返回值表示你访问的实例变量。你通过在第一个参数检索实例对象。实例变量的名字通过C字符串,和第三个参数是void*指针,哪里实例变量的值会被存储。[/color] [color=#231f20] 考虑下面的类:[/color] [color=#231f20] @interface RT8 : NSObject{ NSNumber *val; } @property (nonatomic, assign) NSNumber *val; @end
@implementation RT8 @synthesize val; @end
下面,让我们创建一个新实例,并改变实例变量的值:[/color] [color=#231f20] RT8 *rt8 = [[[RT8 alloc] init] autorelease]; rt8.val = [NSNumber numberWithInt:99];
方位实例变量的值,你可以写类似下面的声明:[/color] [color=#231f20] void *outValue;[/color] [color=#231f20]object_getInstanceVariable(rt8, "val",&outValue); NSLog(@"The number is %d",[(NSNumber*) outValue intValue]);
动态创建新类[/color] [color=#231f20] 如果你的应用程序在运行期间需要创建一个新类,你可以使用运行期间提供的支持,达到你的目标。[/color] [color=#231f20] 创建一个新类,你首先使用函数objc_allocateClassPair()创建它和它的元类。这个函数声明如下:[/color] [color=#231f20] Class objc_allocateClassPair(Class superclass, const char*name, size_t extraBytes)
第一个参数是超类的类对象。第二个参数是新类的C字符串名。第三个和最后一个参数指定的额外的分配字节和通常为0。[/color] [color=#231f20] 创建完新类后,你可以添加实例变量和实例/类方法。然后,你使用函数objc_registerClassPair()通过调用objc_allocateClassPair函数的返回值注册类和它的元类。[/color] [color=#231f20] 让我们通过详细的例子说明这个话题。考虑下面现存的类:[/color] [color=#231f20] @interface RT9 : NSObject{ NSNumber*val; } @property(nonatomic, retain) NSNumber *val; @end
@implementation RT9 @synthesize val; -(id)init{ if(self = [super init]){ self.val = [NSNumber numberWithInt:99]; } return self; } -(void)dealloc{ NSLog(@"dealloc in super"); self.val = nil; [super dealloc]; } @end
RT9是简单的类,我们想使用被叫做MyAwesomeClass新类元类。创建一个新类,我们从下面的声明开始:[/color] [color=#231f20] Class dynaClass = objc_allocateClassPair([RT9 class],"MyAwesomeClass", 0);
然后,我们使用方法class_addIvar添加一个实例变量。声明如下:[/color] [color=#231f20] BOOL class_addIvar(Class cls, const char *name, size_tsize, uint8_t alignment, const char *types)
第一个参数是一个类对象。这个类对象不是一个元类的引用。第二个参数是新变量名的C字符串。第三个参数是新实例变量的大小。第四个参数是对齐的存储。第五个和最后一个是参数是实例变量的编码类型。当且仅当实例变量创建成功函数返回YES。[/color] [color=#231f20] 添加一个实例变量叫做NSArray*类型的vertices,我们写类似下面的东西:[/color] [color=#231f20] class_addIvar(dynaClass,"vertices", sizeof(id), log2(sizeof(id)), "@");
接下来,让我们添加方法many和dealloc:[/color] [color=#231f20] class_addMethod(dynaClass, @selector(many), (IMP)manyImplementation, "@@:"); class_addMethod(dynaClass, @selector(dealloc), (IMP)deallocImplementation, "v@:");
最后,我们注册类和它的原味,如下:[/color] [color=#231f20] objc_registerClassPair(dynaClass);
many方法的实现展示如下:[/color] [color=#231f20]NSNumber* manyImplementation(id self, SEL _cmd){ void *outValue1; void *outValue2; object_getInstanceVariable(self, "vertices", &outValue1); object_getInstanceVariable(self, "val", &outValue2); return [NSNumber numberWithInt: ((NSArray*)outValue1).count + [(NSNumber*)outValue2 intValue]]; }
注意我们怎样访问实例变量,val,从元类继承。[/color] [color=#231f20] dealloc方法的实现如下[/color] [color=#231f20] void deallocImplementation(id self, SEL _cmd){ void *outValue; object_getInstanceVariable(self, "vertices", &outValue); [(id) outValue release]; object_setInstanceVariable(self, "vertices", nil); struct objc_super obS; obS.super_class = [self superclass]; obS.receiver = self; objc_msgSendSuper(&obS, @selector(dealloc)); }
这里,我们恢复NSArray对象和release它。我们传播dealloc到它的父类,使用函数objc_msgSendSuper()发送一个对象到父类,声明如下:[/color] [color=#231f20] id objc_msgSendSuper(struct objc_super*super, SEL op, ...);
第一个参数是objc_super结构,第二个是selector。objc_super结构声明如下:[/color] [color=#231f20] struct objc_super { id receiver; Class super_class; };
下面展示的是怎样使用这个新类:[/color] [color=#231f20] //create an object of this class id dynaObject = [[NSClassFromString(@"MyAwesomeClass") alloc]init]; // OR [dynaClass alloc] //assign a value to an instance variable of this class object_setInstanceVariable(dynaObject, "vertices", [[NSArray arrayWithObjects:@"Bart", @"lisa", nil]retain]); //invoke a method on this class NSNumber *numb = [dynaObject many]; NSLog(@"The returned number is %@", numb); [dynaObject release];
|