因为某些业务需求,项目需要hook所有界面的viewDidAppear方法,我们的方案是使用runtime获取系统所有的类然后将其替换成我们的方法,但是使用这个方法后,在比较小的项目中没什么问题,速度慢的400ms左右,但是在比较大的项目中就严重影响了初始化的速度,在使用了Instruments监测速度的工具后,发现占时比较多的是下面这个方法:
unsignedintoutCount =0;
//这个方法是耗时大户
Class *classLiset = objc_copyClassList(&outCount);
//遍历也是耗时大户
for(unsignedinti =0; i < outCount; i++) {
if([UIViewController Class] == class_getSuperclass(classLiset[i])){
//对抓到的类进行hook
}
free(classLiset);
我们发现 objc_copyClassList(&outCount)方法会获取到系统中的所有类,就算是对初始项目使用这个方法他也能获取到16000个左右的类,而获取到这个16000个类之后对他进行遍历,也就造成了非常严重的耗时,更别说复杂一些的项目了。所以,为了解决这个问题,一开始能想到的方法就是提升遍历的速度,不使用这个普通的for循环,而是使用forin的方法来增强遍历的速度,可是我们发现,这个Class类型是个结构体,不是forin可以遍历的类型,于是乎便放弃了这个想法。
既然没法对遍历进行提速,那么我们需要提速的对象就自然而然的是 objc_copyClassList(&outCount)获取所有类的方法了,回顾我们的需求,是获取所有控制器,之后替换他们的viewDidAppear方法,那么,我们用objc_copyClassList(&outCount)获取系统的所有类是否是获取太多了呢?毕竟,我们需要的也就是我们用到的一些类,而用了这个方法,不管用没用到,系统中的所有类都会被抓到,于是,我就在想,有没可以获取自己创建的类的方法,毕竟比起获取系统类的最少16000的量级,获取自己的类就会少非常多,于是乎google了一下,还真的找到了这样的方法
#import <dlfcn.h>
#import <mach-o/ldsyms.h>
unsigned int count;
const char **classes;
Dl_info info;
//1.获取app的路径
dladdr(&_mh_execute_header, &info);
//2.返回当前运行的app的所有类的名字,并传出个数
//classes:二维数组 存放所有类的列表名称
//count:所有的类的个数
classes = objc_copyClassNamesForImage(info.dli_fname, &count);
for (int i = 0; i < count; i++) {
NSString *className = [NSString stringWithCString:classes[i] encoding:NSUTF8StringEncoding];
if (![className isEqualToString:@""] && className) {
Class class = NSClassFromString(className){
//做些操作
}
}
}
在使用这个方法后,获取的就成了开发者创建出的类,数量少了非常多,方法的耗时减少,后面的遍历也就变的不怎么耗时了,唯一稍微有点耗时的就是 NSString *className = [NSString stringWithCString:classes[i] encoding:NSUTF8StringEncoding]将string转为类的这个方法了,但是比起之前那1S左右的初始化,这个速度已经无伤大雅了~