14 反射
Tags: Objective-C
OC中的反射跟Java中的一个道理,一般我们理解为动态调用,下面有几个名字概念。
Class对象:Class类型的对象,这些对象是各个类,假设有Person类、Man类、Woman类三个类,那么Class类型的对象就可以是Person类、Man类、Woman类。
SEL对象:SEL类型的对象,这些对象是各个方法,OC中也称之为selector,表示具体的各个函数。
Protocol对象:Protocol类型的对象,这些对象是各个协议,表示具体的协议。
IMP对象:IMP类型的对象,也称为函数指针对象,表示某个具体函数的函数指针。IMP类型表示参数类型和返回类型指定的函数指针,比如某个IMP类型要求函数的参数为int,返回值为int,假设有三个函数int a(int), int b(int), int c(int),那么这三个函数的指针都可以叫做该IMP类型的对象。
14.1 获取Class对象、SEL对象、Protocol对象、IMP对象
获取Class对象的有三种方式:
1. 使用Class NSClassFromString(NSString *aClassName)
函数获取Class对象,参数是字符串类型,表示某个类的名字。
2. 使用[类名 class]
方法获取对应的类的Class对象。
3. 使用[目标对象 class]
方法获取目标对象的对应的类的Class对象。
获取SEL对象的有两种方式:
1. 使用@selector(方法名)
指令来获取对应方法的SEL对象。
2. 使用SEL NSSelectorFromString(NSString *aSelectorName)
函数通过方法名的字符串获取SEL对象。
获取Protocol对象有两种方式:
1. 使用@protocol(协议名)
指令来获取对应协议的Protocol对象。
2. 使用Protocol *NSProtocolFromString(NSString *namestr)
函数通过协议名字符串获取Protocol对象。
获取IMP对象的的方法如下:
//①定义函数指针
//该函数指针的类型为 returnType (*) (id, SEL, 形参类型1, 形参类型2, ...)
returnType (函数指针变量 *) (id, SEL, 形参类型1, 形参类型2, ...);
//②获取IMP对象,id表示某个类的对象,SEL表示该类的某个方法,整个表示指向某个对象下某个方法的函数指针
函数指针变量 = (returnType (*) (id, SEL, 形参类型1, 形参类型2, ...))[object methodForSelector:SEL对象]
//③使用IMP对象
returnType a = 函数指针变量(object, SEL对象, 实参1, 实参2, ...);
获得Class对象的示例代码如下:
ClassTest.m
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
//通过字符串来获取Class对象
Class clazz = NSClassFromString(@"NSDate");
NSLog(@"%@", clazz);
//直接使用Class对象来创建对于的类的对象
id date = [[clazz alloc] init];
NSLog(@"%@", date);
//通过对象来获取Class对象
NSLog(@"%@", [date class]);
//通过类来获取Class对象
NSLog(@"%d", clazz == [NSDate class]);
}
return 0;
}
运行结果如下:
NSDate
2015-12-01 12:27:16 +0000
__NSTaggedDate
1
14.2 检查继承关系
需要检查某个对象是否是某个类或其子类的实例,或者该对象是否遵守某个协议,称之为检查继承关系。
1. [对象 isKindOfClass:Class对象] 对象是否是该Class对象类及其子类的实例。
2. [对象 isMemberOfClass:Class对象] 对象是否是该Class对象类的实例。
3. [对象 conformsToProtocol:Protocol对象] 对象是否遵守了Protocol对象的协议及子协议。
代码例子如下:
GKHEatable.h
#import <Foundation/Foundation.h>
@protocol GKHEatable <NSObject>
- (void) eat:(NSString *)food;
@end
GKHApple.h
#import <Foundation/Foundation.h>
#import "GKHEatable.h"
@interface GKHApple : NSObject <GKHEatable>
@end
GKHApple.m
#import "GKHApple.h"
@implementation GKHApple
- (void) eat:(NSString *)food {
NSLog(@"我在吃%@", food);
}
@end
CheckObject.m
#import <Foundation/Foundation.h>
#import "GKHApple.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
GKHApple *apple = [[GKHApple alloc] init];
NSLog(@"%@", [apple class]);//GKHApple
//判断对象是否是某个类的实例
NSLog(@"apple是否是GKHApple的实例:%d", [apple isMemberOfClass:[GKHApple class]]);//1
NSLog(@"apple是否是NSObject的实例:%d", [apple isMemberOfClass:NSObject.class]);//0
//判断对象是否是某个类及其子类的实例
NSLog(@"apple是否是GKHApple及其子类的实例:%d", [apple isKindOfClass:[GKHApple class]]);//1
NSLog(@"apple是否是NSObject及其子类的实例:%d", [apple isKindOfClass:NSObject.class]);//1
//判断对象是否遵守了指定协议
NSLog(@"apple是否遵守了GKHEatable协议:%d", [apple conformsToProtocol:@protocol(GKHEatable)]);//1
NSLog(@"apple是否遵守了GKHEatable协议:%d", [apple conformsToProtocol:NSProtocolFromString(@"NSObject")]);//1
}
return 0;
}
14.3 动态调用方法
程序需要判断某个对象是否可调用某个方法,可以通过NSObject提供的如下方法:
[对象 respondsToSelector:SEL对象]
,如果可以调用返回YES,不可以返回NO。
程序需要动态调用某个对象的方法,可以通过如下方式:
1. [对象 performSelector:SEL对象 withObject:实参]
2. objc_msgSend(对象, SEL对象, 实参列表...)
函数,对象是方法调用者,SEL对象是具体的方法,实参列表是方法的参数。使用这个函数需要添加头文件#import <objc/message.h>
代码示例如下:
GKHCar.h
@interface GKHCar : NSObject
- (void) move:(NSNumber *)count;
- (int) addSpeed:(int)factor;
@end
GKHCar.m
#import <objc/message.h>
#import "GKHCar.h"
@implementation GKHCar
//汽车前进count距离
- (void) move:(NSNumber *)count {
int num = [count intValue];
for (int i = 0; i < num; ++i) {
NSLog(@"%@", [NSString stringWithFormat:@"汽车正在路上走~~~%d", i]);
}
}
//汽车加速
- (int) addSpeed:(int)factor {
//使用performSelector:动态调用move:方法
[self performSelector:@selector(move:) withObject:[NSNumber numberWithInt:2]];
//使用objc_msgSend()函数动态调用move:方法
objc_msgSend(self, @selector(move:), [NSNumber numberWithInt:4]);
objc_msgSend(self, NSSelectorFromString(@"move:"), [NSNumber numberWithInt:5]);
NSLog(@"汽车正在加速。。。%d", factor);
return 100 * factor;
}
@end
main.m
#import <Foundation/Foundation.h>
#import <objc/message.h>
#import "GKHCar.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
//获取GKHCar的Class对象
Class clazz = NSClassFromString(@"GKHCar");
//动态创建GKHCar对象
id car = [[clazz alloc] init];
//使用performSelector:方法动态调用car的addSpeed:方法
[car performSelector:NSSelectorFromString(@"addSpeed:") withObject:[NSNumber numberWithInt:6]];
//使用objc_msgSend()函数动态调用car的addSpeed:方法
objc_msgSend(car, @selector(addSpeed:), [NSNumber numberWithInt:6]);
//使用IMP对象,也即函数指针动态调用car的addSpeed:方法
//定义函数指针
//typedef int(*FuncPointer)(id, SEL, int);
//将car对象的addSpeed方法赋给函数指针p
//FuncPointer p = (FuncPointer)[car methodForSelector:@selector(addSpeed:)];
//用函数指针p调用car的方法
//int result = p(car, NSSelectorFromString(@"addSpeed:"), 6);
int (*p) (id, SEL, int);
p = (int (*)(id, SEL, Int))[car methodForSelector:@selector(addSpeed:)];
int result = p(car, @selector(addSpeed:), 6);
NSLog(@"加速后的速度为:%d", result);
}
return 0;
}
工程需要将Enable Strict Checking of objc_msgSend Calls设置为NO,否则会报错:
Too many arguments to function call, expected 0, have 3