IOS: runtime 学习(1)

相关定义

// 类中的一个方法
typedef struct objc_method *Method;

// 实例变量
typedef struct objc_ivar *Ivar;

// 类别Category
typedef struct objc_category *Category;

// 类中声明的属性
typedef struct objc_property *objc_property_t;

// 类在runtime中的表示
struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;  // 指针,顾名思义,表示是一个什么,

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;  // 指向父类
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;  // 类名
    long version                                             OBJC2_UNAVAILABLE;  
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE; // 成员变量列表
    struct objc_method_list * _Nullable * _Nullable methodLists    // 方法列表                 OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE; // 缓存
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE; // 协议列表
#endif

} OBJC2_UNAVAILABLE;

使用

获取方法/属性/协议列表*

我们可以通过runtime的一系列方法获取类的一些信息,如属性列表,方法列表,成员变量列表,和遵循的协议列表。

给当前的类导入头文件#import

 unsigned int count;
        //获取属性列表
        objc_property_t *propertyList = class_copyPropertyList([self class], &count);
        for (unsigned int i=0; i < count; i++) {
            const char *propertyName = property_getName(propertyList[i]);
            NSLog(@"self.property---->%@", [NSString stringWithUTF8String:propertyName]);
        }

        //获取方法列表
        Method *methodList = class_copyMethodList([self class], &count);
        for (unsigned int i=0; i < count; i++) {
            Method method = methodList[i];
            NSLog(@"self.method---->%@", NSStringFromSelector(method_getName(method)));
        }

        //获取成员变量列表
        Ivar *ivarList = class_copyIvarList([self class], &count);
        for (unsigned int i=0; i < count; i++) {
            Ivar myIvar = ivarList[i];
            const char *ivarName = ivar_getName(myIvar);
            NSLog(@"self.Ivar---->%@", [NSString stringWithUTF8String:ivarName]);
        }

        //获取协议列表
        __unsafe_unretained Protocol **protocolList = class_copyProtocolList([self class], &count);
        for (unsigned int i=0; i < count; i++) {
            Protocol *myProtocal = protocolList[i];
            const char *protocolName = protocol_getName(myProtocal);
            NSLog(@"self.protocol---->%@", [NSString stringWithUTF8String:protocolName]);
        }

方法调用

如果用实例对象调用实例方法,会到实例的isa指针指向的对象(也就是类对象)操作。
如果调用的是类方法,就会到类对象的isa指针指向的对象(也就是元类对象)中操作。

1、首先,在相应操作的对象中的缓存方法列表中找调用的方法,如果找到,转向相应实现并执行。
2、如果没找到,在相应操作的对象中的方法列表中找调用的方法,如果找到,转向相应实现执行
3、如果没找到,去父类指针所指向的对象中执行1,2.
4、以此类推,如果一直到根类还没找到,转向拦截调用。
5、如果没有重写拦截调用的方法,程序报错。

拦截调用

在方法调用中说到了,如果没有找到方法就会转向拦截调用。
那么什么是拦截调用呢。
拦截调用就是,在找不到调用的方法程序崩溃之前,你有机会通过重写NSObject的四个方法来处理。

+ (BOOL)resolveClassMethod:(SEL)sel;
+ (BOOL)resolveInstanceMethod:(SEL)sel;
//后两个方法需要转发到其他的类处理
- (id)forwardingTargetForSelector:(SEL)aSelector;
- (void)forwardInvocation:(NSInvocation *)anInvocation;

第一个方法是当你调用一个不存在的类方法的时候,会调用这个方法,默认返回NO,你可以加上自己的处理然后返回YES。
第二个方法和第一个方法相似,只不过处理的是实例方法。
第三个方法是将你调用的不存在的方法重定向到一个其他声明了这个方法的类,只需要你返回一个有这个方法的target。
第四个方法是将你调用的不存在的方法打包成NSInvocation传给你。做完你自己的处理后,调用invokeWithTarget:方法让某个target触发这个方法。

动态添加方法

重写了拦截调用的方法并且返回了YES,我们要怎么处理呢?
有一个办法是根据传进来的SEL类型的selector动态添加一个方法。

首先从外部隐式调用一个不存在的方法:

 [model performSelector:@selector(resolveAdd:) withObject:@"test"];

然后,在model对象内部重写拦截调用的方法,动态添加方法。

void runAddMethod(id self, SEL _cmd, NSString *string){
    NSLog(@"add C IMP  %@", string);
}

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    //给本类动态添加一个方法
    if ([NSStringFromSelector(sel) isEqualToString:@"resolveAdd:"]) {
        class_addMethod(self, sel, (IMP)runAddMethod, "v@:*");
    }
    return YES;
}

其中class_addMethod的四个参数分别是:

Class cls 给哪个类添加方法,本例中是self
SEL name 添加的方法,本例中是重写的拦截调用传进来的selector。
IMP imp 方法的实现,C方法的方法实现可以直接获得。如果是OC方法,可以用+ (IMP)instanceMethodForSelector:(SEL)aSelector;获得方法的实现。
“v@:*”方法的签名,代表有一个参数的方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值