14 反射

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

屏幕快照 2015-12-01 21.19.34.png-75.8kB

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值