分享

<objc/rumtime.h>

 昵称8455184 2011-12-31

iOS4高级编程 第二章 2.13.4.3 Objective-C 运行

获取一个类中定义的所有方法

如果你想获取一个类中定义的所有方法的数组,不包含任何它的超类,你可以使用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)), "@");

接下来,让我们添加方法manydealloc[/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结构,第二个是selectorobjc_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];

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多