首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

RunTime(2)

RunTime(2)

runtime几种用途

    方法交换

举个例子,比如[UIImage imageNamed"1"],那么这张图片有没有也不知道,那么我们该怎么做呢?

    可以建一个自定义的类,每次调用自己的类,但是这样每次都需要导入头文件,而且如果项目已经维护了好久,那么这种方法会改起来没麻烦
    可以写个类别,但是问题同上

因此,这时候我们需要想到runtime方法交换,因为我们不想改动之前的代码,但是之前的代码不符合我们需求,我们需要扩展,所以我们应该想到这种方法.
那么需要将imageNamed系统方法实现IMP 和 自定义的 gl_ imageNamedIMP 交换

     //新建一个类别
    //值调用一次,在加载类的时候调用
    + (void)load{
        
        Method method1 = class_getClassMethod([self class], @selector(imageNamed);
        Method method2 = class_getClassMethod([self class], @selector(gl_imageNamed);
     
        
        method_exchangeImplementations(method1, method2);
        
    }
     
    //会调用多次,可以用单利来限制代码执行次数,因为swift里没有+load,所以只能用+initialize
    + (void)initialize{
        
    }
     
    + (UIImage *)gl_imageNamedNSString *)name{
     
        UIImage *image = [self gl_imageNamed:name];
        
        if (image) {
            NSLog(@"图片存在");
        } else {
            NSLog(@"图片不存在");
        }
        
        return image;
    }
     

其中我说明了load方法和initialize 的区别
这样我在调用UIImage *image = [UIImage imageNamed"qq"];会告诉我图片没有

    动态添加方法

1.方法调用流程:
1)通过isa 指针去对应的类中去找方法,对象方法去类对象的方法列表去找方法,类方法去元类的方法列表中去找方法.
2)注册方法编号
3)根据方法编号去查找方法
4)找到最终函数实现地址

2.运行时添加一个方法,我再举个🌰:
我建一个Person类,接着调用

    Person *p = [[Person alloc] init];
    [p performSelectorselector(eat)];

为什么用performSelector,因为对于没有声明的方法,编译时无法编过去, performSelector是运行时执行,接着运行一下,crash!
reason: '-[Person eat]: unrecognized selector sent to instance 0x604000002220'
我们可以拦截这个崩溃,在Person.m里添加

    void eat(id self,SEL _cmd){
        NSLog(@"吃了");
    }
     
    //第一次拦截,其实有三次转发
    + (BOOL)resolveInstanceMethodSEL)sel{
        if ([NSStringFromSelector(sel) isEqualToString"eat"]) {
            class_addMethod([self class], sel, (IMP)eat, "v@:");
            return YES;
        }
        return [super resolveInstanceMethod:sel];
    }

class_addMethod 方法最后一参数在苹果文档中查找
v:代表返回值是void
@:代表参数是id
::代表参数是 sel
void eat(id self,SEL _cmd){ NSLog(@"吃了"); }:该函数的两个参数,是默认传的,其实苹果每个函数调用都会传self和_cmd,只是编译器帮我们做了.
返回列表