简单回顾一下这部分知识点:对于已经存在的类我们用class_addProperty方法来添加属性,而对于动态创建的类我们通过class_addIvar添加属性, 它会改变一个已有类的内存布局,一般是通过objc_allocateClassPair动态创建一个class,才能调用class_addIvar创建Ivar,最后通过objc_registerClassPair注册class。Pair中文一对的意思,我只创建一个类,为什么这两个方法名会出现Pair这个词呢?这是因为创建类的时候也会创建元类。
// 动态创建类
-(void)creatClass {
NSString *className = @"dynaminClass";
if (!NSClassFromString(className)) {
Class newClass = objc_allocateClassPair(NSString.class, className.UTF8String, 0);
class_addMethod(newClass, @selector(test), (IMP)funcIMP, "v@:");
objc_registerClassPair(newClass);
}
}
// 生成类的实例
-(void)creatInstance {
Class newClass = NSClassFromString(@"dynaminClass");
id intanceOfClass = [[newClass alloc] init];
// [intanceOfClass eat]; 编译不通过
[intanceOfClass performSelector:@selector(func)];
}
// 调用方法
void funcIMP(Class self, SEL _cmd)
{
Class class = [self class];
// 类: dynaminClass, sel: test.
NSLog(@"self: %@ sel: %s", class, _cmd);
}
复制代码
让咱们回到问题上,如果把创建类的if条件去掉,然后类名改为一个已有的类名,会发生什么呢? 大家肯定都知道,但是这里咱们先猜测一下:
1、不会crash,试想一下,一对双胞胎,外貌衣服都一样,我想要找某一个,那肯定不知道这两个哪个是。类比runtime,你给我创建了两个一样的类,就算能创建并注册成功,我应该找哪个呢?runtime表示我也很懵啊,会导致runtime混乱,产生出乎意料的问题。
2、会crash
尝试运行一下,不出意料,果然崩了,原因:EXC_BAD_ACCESS,这个问题相信很多人都头疼,别提了,我想静静?。咱们继续探索,runtime相信大家都了解,看一下继承链,实例->类>元类>根元类,其中只有实例能创建多个(地址不同),现在我动态注册了一个类,类名是已存在的类名,能创建成功并开辟内存吗?并不能,objc_allocateClassPair函数没crash的原因是内部返回直接return了,没走正常的逻辑,而且也不会崩溃,因为没有内存操作。而objc_registerClassPair注册操作会操作内存,崩溃位置在获取data函数(需要跑源码):
class_rw_t* data() const {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
复制代码
因为类根本没在内存中,所以内存的操作肯定会报EXC_BAD_ACCESS(坏内存访问)。
总结:源码就不带大家看了,内部没什么特别的操作,讲这个的原因是我之前面试的时候碰到了。我在网上看面试题也没看到过这个问题,所以就当是为了面试的朋友们做个简单的解释吧。主要有两个考点:
1、动态创建类的时候只会创建一个类吗?不是的,还有对应的元类(源码中可以看到)
2、动态添加类和已有类重名会发生什么?crash,EXC_BAD_ACCESS(坏内存访问)。