本文共 3278 字,大约阅读时间需要 10 分钟。
在运行时动态创建一个类:
导入头文件#import <objc/runtime.h>
,动态添加类,创建一个继承 NSString
的类NSStringSubClass
类,如下代码:
// 类名也可以直接使用C字符串写法 ”NSStringSubClass“ NSString *className = @"NSStringSubClass"; // Creates a new class and metaclass. Class newClass = objc_allocateClassPair(NSString.class, className.UTF8String, 0); class_addMethod(newClass, @selector(eat), (IMP)EatFunction, "v@:"); objc_registerClassPair(newClass);
分析上述代码,以上述代码为例:
第一步:调用objc_allocateClassPair()
函数,对类对(class and metaClass)进行分配内存,Pair
的意思就是一对。三个参数,一是父类:NSString
类;二是类名称:“NSStringSubClass”;三是额外字节:0。
小知识:C字符串和OC字符串互相转换:
const char *cStr = "哈哈"; NSString *str = @"嘻嘻"; NSString *cToStr = [NSString stringWithCString:cStr encoding:NSUTF8StringEncoding]; const char *strToC = str.UTF8String;
第二步:调用class_addMethod()
函数,动态为类添加方法。四个参数,一是给哪个类添加方法:newClass
类;二是方法名:eat
;三是方法实现:EatFunction
;四是类型:“v@:”
,具体的意思查看,或者查看<runtime.h>文件第1791行处。
小知识:快捷键command+shift+o
:查看方法所在类;command+shift+0
:查看开发文档。
第三步:调用objc_registerClassPair()
注册该类,一个参数:newClass
。
添加的方法使用名为EatFunction实现的函数,其定义如下:
void EatFunction(id self, SEL _cmd){ NSLog(@"This object is %p.", self); NSLog(@"Class is %@, and super is %@.", [self class], [self superclass]); Class currentClass = [self class]; Class superClass = [self superclass]; for (int i = 0; i < 5; i++) { NSLog(@"Following the isa classPointer:%p class:%@ superClassPointer:%p superClass:%@", currentClass, currentClass, superClass,superClass); currentClass = object_getClass(currentClass); superClass = class_getSuperclass(superClass); } NSLog(@"NSObject's class is %p", [NSObject class]); NSLog(@"NSObject's meta class is %p", object_getClass([NSObject class])); // NSLog(@"NSObject's meta class is %p", objc_getMetaClass(NSStringFromClass([NSObject class]).UTF8String));}
在中我们说过,实例对象的本质是一个指向该对象真实类型的 isa 指针,即指向该实例对象的类,那么怎么获取该类呢?上例中的代码提到,通过函数object_getClass()
可以获取到,该函数的参数是id(任意对象)类型。之前我们还说过,类也是对象,类的本质是objc_class
结构类型的指针,在创建类的时候会生成一个isa指针,该指针指向metaClass(该类的元类),我们同样通过函数object_getClass()
可以获取到该类的元类。有人会有疑问,这个函数的参数是id类型的对象啊,可以把类当作参数?答案是肯定的,因为类也是对象,当我们把Class类型当作参数时,获取就是该类的元类。口说无凭,我们来执行之前添加的EatFunction()
函数,首先创建实例对象:
id intanceOfClass = [[newClass alloc] init];// [intanceOfClass eat]; 编译不通过[intanceOfClass performSelector:@selector(eat)];
由于没有声明eat方法,因此我使用performSelector:
来调用它,避免编译器发出错误。
执行结果:
This object is 0x6000012045f0.Class is NSStringSubClass, and super is NSString.Following the isa classPointer:0x600001e43270 class:NSStringSubClass superClassPointer:0x109fb44c8 superClass:NSStringFollowing the isa classPointer:0x600001e430f0 class:NSStringSubClass superClassPointer:0x10a959ec8 superClass:NSObjectFollowing the isa classPointer:0x10a959e78 class:NSObject superClassPointer:0x0 superClass:(null)Following the isa classPointer:0x10a959e78 class:NSObject superClassPointer:0x0 superClass:(null)Following the isa classPointer:0x10a959e78 class:NSObject superClassPointer:0x0 superClass:(null)NSObject's class is 0x10a959ec8NSObject's meta class is 0x10a959e78
分析结果:
intanceOfClass对象的地址:0x6000012045f0; NSStringSubClass类的地址:0x600001e43270; NSStringSubClass元类的地址:0x600001e43270; NSStringSubClass元类的isa指针指向的元类地址:0x10a959e78,即指向NSObject类的元类; NSObject类的元类指向的是它的元类自己;这也印证了我们上面所说,同时上篇文章中那幅isa指针和super_class指针的指向图也能印证。转载地址:http://igivi.baihongyu.com/