Runtime(一)类与对象

一.简介
objective-c语言是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了运行时来处理。这种动态语言的优势在于:我们写代码时能够更灵活性,如果我们可以把消息转发给我们想要的对象,或者随意交换一个方法的实现等。

这种特性意味着objective-c不仅需要一个编译器,还需要一个运行时系统来执行编译的代码。对于objective-c来说,这个运行时系统就像一个操作系统一样,它让所有的工作可以正常的运行。这个运行时系统即Objc Runtime。Objc Runtime其实是一个Runtime库,它基本上是用c和汇编写的,这个库使得c语言有了面向对象的能力。

Runtime库主要做下面几件事:
封装:在这个库中,对象可以用c语言中的结构体表示,而方法可以用c函数来实现,另外再加上了一些额外的特性。这些结构体和函数被runtime函数封装后,我们就可以在程序运行时创建,检查,修改类,对象和它们的方法了。

找出方法的最终执行代码:当程序执行[object doSomething]时,会向消息接收者(object)发送一条消息(doSomething),runtime会根据消息接收者是否能响应消息而做出不同的反应。后面详细介绍。

runtime目前有两个版本:Modern runtime和Legacy runtime。Modern Runtime 覆盖了64位的Mac OS X Apps,还有 iOS Apps,Legacy Runtime 是早期用来给32位 Mac OS X Apps 用的,也就是可以不用管就是了。

二.类与对象基础数据结构
1.Class
objective-c类是由Class类型来表示的,它实际上是一个指向objc_class结构体的指针。它的定义如下:

typedef struct objc_class *Class;

objc_class结构体的定义如下:

struct objc_class{
    Class isa OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    //父类
    Class super_class; 
    //类名
    const char *name;
    //类的版本信息,默认为0
    long version;
    //类信息,供运行期使用的一些位标识
    long info;
    //该类的实例变量大小 
    long instance_size;
    //该类的成员变量链表
    struct objc_ivar_list *ivars;
    //方法定义的链表
    struct objc_method_list **methodLists;
    //方法缓存
    struct objc_cache *cache;
    //协议链表
    struct objc_protocol_list *protocols;           
#endif
} OBJC2_UNAVAILABLE;

在这个定义中,下面几个字段是我们感兴趣的:
1.isa:需要注意的是在objective-c中,所有的类自身也是一个对象,这个对象的Class里面也有一个isa指针,它指向metaClass(元类),我们会在后面介绍它。
2.super_class:指向该类的父类,如果该类已经是最顶层的根类(如NSObject或NSProxy),则super_class为NULL。
3.cache:用于缓存最近使用的方法。一个接收者对象接收到一个消息时,它会根据isa指针去查找能够响应这个消息的对象。在实际使用中,这个对象只有一部分方法是常用的,很多方法其实很少用或者根本用不上。如果每次消息来时,我们都是methodLists中遍历一遍,性能势必很差。这时,cache就派上用场了。在我们每次调用过一个方法后,这个方法就会被缓存到cache列表中,下次调用的时候runtime就会先去cache中查找,如果cache没有,才去methodLists中查找方法。这样,对于那么经常用到的方法的调用,提高了调用的效率。
4.version:我们可以使用这个字段来提供类的版本信息。这对于对象的序列化非常有用,它可以让我们识别出不同类定义版本中实例变量布局的改变。

三.objc_object与id
objc_object是表示一个类的实例的结构体。

struct objc_object{
    Class isa OBJC_ISA_AVAILABILITY;
};
typedef struct objc_object *id;

可以看到,这个结构体只有一个字段,即指向其类的isa指针。这样,当我们向一个objective-c对象发送消息时,运行时库会根据实例对象的isa指针找到这个实例对象所属的类。Runtime库会在类的方法列表及父类的方法列表中去寻找与消息对应的selector指向的方法。找到后即运行这个方法。

当创建一个特定类的实例对象时,分配的内存包含一个objc_object数据结构,然后是类的实例变量的数据。

NSObject类的alloc和allocWithZone:方法使用函数class_createInstance来创建objc_object数据结构。

另外还有我们常见的id,它是一个objc_object结构类型的指针。它的存在可以让我们实现类似于c++中泛型的一些操作。该类型的对象可以转换为任何一种对象,有点类似于c语言中void*指针类型的作用。

四.objc_cache
上面提到了objc_class结构体中的cache字段,它用于缓存调用过的方法。这个字段是一个指向objc_cache结构体的指针,其定义如下:

struct objc_cache{
    unsigned int mask;  /* total = mask + 1 */
    unsigned int occupied;
    Method buckets[1];
};

该结构体的字段描述如下:
mask:一个整数,指定分配的缓存bucket的总数。在方法查找过程中,objective-c runtime使用这个字段来确定开始线性查找数组的索引位置。指向方法selector的指针与该字段做一个AND位操作(index = (mask & selector))。这可以作为一个简单的hash散列表算法。
occupied:一个整数,指定实际占用的缓存bucket的总数。
buckets:指向Method数据结构指针的数组。这个数组可能包含不超过mask + 1个元素。需要注意的是,指针可以是NULL,表示这个缓存bucket没有被占用,另外被占用的bucket可能是不连续的。这个数组可能会随着时间而增长。

五.元类(Meta Class)
在上面我们提到,所有的类自身也是一个对象,我们可以向这个对象发送消息(即调用类方法)。如:

NSArray *array = [NSArray array];

这个例子中,+array消息发送给了NSArray类,而这个NSArray也是一个对象。既然是对象,那么它也是一个objc_object指针,它包含一个指向其类的一个isa指针。那么这就有一个问题了,这个isa指针指向什么呢?为了调用+array方法,这个类的isa指针必须指向一个包涵这些类方法的一个objc_class结构体。这就引出了meta-class的概念:
meta-class是一个类对象的类。
当我们向一个对象发送消息时,runtime会在这个对象所属的这个类 的方法列表中查找方法;而向一个类发送消息时,会在这个类的meta-class的方法列表中查找。
meta-class之所以重要,是因为它存储着一个类的所有类方法。每个类都会有一个单独的meta-class,因为每个类的类方法基本不可能完全相同。

再深入一下,meta-class也是一个类,也可以向它发送一个消息,那么它的isa又是指向什么呢?为了不让这种结构无限延伸下去,objective-c的设计者让所有的meta-class的isa指向基类的meta-class,以此作为它们的所属类。即,任何NSObject继承体系下的meta-class都使用NSObject的meta-class作为自己的所属类,而基类的meta-class的isa指针是指向它自己。这样就形成了一个完美的闭环。
通过上面的描述,再加上对objc_class结构体中super_class指针的分析,我们就可以描述出类及相应meta-class类的一个继承体系了,如下图所示:
这里写图片描述
对于NSObject继承体系来说,其实例方法对系统中的所有实例,类和meta-class都是有效的;而类方法对于体系内的所有类和meta-class都是有效的。

例子:

void TestMetaClass(id self,SEL _cmd){

    NSLog(@"This object is %p",self);
    NSLog(@"Class is %@",[self class]);
    NSLog(@"Super Class is %@",[self superclass]);

    Class currentClass = [self class];
    for (int i=0; i< 4; i++) {
        NSLog(@"Following the isa pointer %d times gives %p", i, currentClass);
        currentClass = objc_getClass((__bridge void *)currentClass);
    }
    NSLog(@"NSObject's class is %p", [NSObject class]);
    NSLog(@"NSObject's meta class is %p", objc_getClass((__bridge void *)[NSObject class]));

}

@implementation Test

- (void)ex_registerClassPair {

    Class newClass = objc_allocateClassPair([NSError class], "TestClass", 0);
    class_addMethod(newClass, @selector(testMetaClass), (IMP)TestMetaClass, "v@:");
    objc_registerClassPair(newClass);

    id instance = [[newClass alloc] initWithDomain:@"some domain" code:0 userInfo:nil];
    [instance performSelector:@selector(testMetaClass)];
}

@end

这个例子是在运行时创建一个NSError的子类TestClass,然后为这个子类添加一个方法testMetaClass,这个方法的实现是TestMetaClass函数。

打印结果是

2016-05-17 11:35:13.904 类与对象[830:79889] This object is 0x100503510
2016-05-17 11:35:13.905 类与对象[830:79889] Class is TestClass
2016-05-17 11:35:13.905 类与对象[830:79889] Super Class is NSError
2016-05-17 11:35:13.905 类与对象[830:79889] Following the isa pointer 0 times gives 0x1005032b0
2016-05-17 11:35:13.905 类与对象[830:79889] Following the isa pointer 1 times gives 0x1005032b0
2016-05-17 11:35:13.906 类与对象[830:79889] Following the isa pointer 2 times gives 0x1005032b0
2016-05-17 11:35:13.906 类与对象[830:79889] Following the isa pointer 3 times gives 0x1005032b0
2016-05-17 11:35:13.906 类与对象[830:79889] NSObject's class is 0x7fff7a7910f0
2016-05-17 11:35:13.906 类与对象[830:79889] NSObject's meta class is 0x0

我们在for循环中,我们通过objc_getClass来获取对象的isa,并将其打印出来,依次一直回溯到NSObject的meta-class。分析打印结果,可以看到最后指针指向的地址是0x0,即NSObject的meta-class的类地址。
这里需要注意的是:我们在一个类对象调用class方法是无法获取meta-class,它只是返回类而已。

六.类相关操作函数
1.类名操作的函数主要有:

//获取类的类名
const char *class_getName(Class cls);

对于class_getName函数,如果传入的cls为Nil,则返回一个字字符串。
2.父类和元类操作的函数主要有:

//获取类的父类
Class class_getSuperclass(Class cls);
//判断给定的Class是否是一个元类
BOOL class_isMetaClass(Class cls);

class_getSuperclass函数,当cls为Nil或者cls为根类时,返回Nil。不过通常我们可以使用NSObject类的superclass方法来达到同样的目的。
class_isMetaClass函数,如果是cls是元类,则返回YES;如果否或者传入的cls为Nil,则返回NO。

3.实例变量大小操作的函数有:

//获取实例大小
size_t class_getInstanceSize()

七.成员变量(ivars)及属性
1.成员变量操作函数,主要包含以下函数:

//获取类中指定名称实例成员变量的信息
Ivar class_getInstanceVariable(Class cls,const char *name);

//获取类成员变量的信息
Ivar class_getClassVariable(Class cls,const char *name);

//添加成员变量
BOOL class_addIvar(Class cls,const char *name,size_t size,uint8_t alignment,const char *types );

//获取整个成员变量列表
Ivar *class_copyIvarList(Class cls,unsigned int *outCount);

class_getInstanceVariable函数,它返回一个指向包含name指定的成员变量的objc_ivar结构体的指针(Ivar)。

class_getClassVariable函数,目前没有找到关于objective-c中类变量的信息,一般认为objective-c不支持类变量。注意,返回的列表不包含父类的成员变量和属性。

objective-c不支持往已存在的类中添加实例变量,因此不管是系统库提供的类,还是我们自定义的类,都无法动态添加成员变量。但如果我们通过运行时来创建一个类的话,又应该如何给它添加成员变量呢?这时我们就可以使用class_addIvar函数了。不过需要注意的是,这个方法只能在objc_allocateClassPair函数与objc_registerClassPair之间调用。另外,这个类也不能是元类。成员变量按字节最小对齐量是1<

//获取指定的属性
objc_property_t class_getProperty(Class cls,const char *name);

//获取属性列表
objc_property_t *class_copyPropertyList(Class cls,unsigned int *outCount);

//为类添加属性
BOOL class_addProperty ( Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount );

//替换类的属性
void class_replaceProperty (Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount);

这一种方法也是针对ivars来操作,不过只操作那些是属性的值。

3.方法操作主要有以下函数:

//添加方法
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types);

//获取实例方法
Method class_getInstanceMethod(Class cls,SEL name);

//获取类方法
Method class_getClassMethod(Class cls,SEL name);

//获取所有方法的数组
Method *class_copyMethodList(Class cls, unsigned int *outCount);

//替代方法的实现
IMP class_replaceMethod (Class cls, SEL name, IMP imp, const char *types);

//返回方法的具体实现
IMP class_getMethodImplementation(Class cls, SEL name);
IMP class_getMethodImplementation_stret(Class cls, SEL name);

//类实例是否响应指定的selector
BOOL class_respondsToSelector(Class cls, SEL sel);

class_addMethod的实现会覆盖父类的方法实现,但不会取代本类中已存在的实现,如果本类中包含一个同名的实现,则函数会返回NO。如果要修改已存在实现,可以使用method_setImplementation。一个objectvie-c方法是一个简单的c函数,它至少包含两个参数——self和_cmd。所以,我们的实现函数(IMP参数指向的函数)至少需要两个参数,如下所示:

void myMehtodIMP(id self,SEL _cmd){

}

与成员变量不同的是,我们可以为类动态添加方法,不管这个类是否存在。
另外,参数types是一个描述传递给方法的参数类型的字符数组。
class_getInstanceMethod,class_getClassMethod函数,与class_copyMethodList不同的是,这两个函数都会去搜索父类的实现。

class_copyMethodList函数,返回包含所有实现方法的数组,如果需要获取类方法,则可以使用class_copyMethodList(objc_getClass(cls),&count)(一个类的实例方法是定义在元类里面)。该列表不包含父类实现的方法。outCount参数返回方法的个数。在获取到列表后,我们需要使用free()方法来释放它。

class_replaceMethod函数,该函数的行为可以分为两种:如果类中不存在name指定的方法,则类似于class_addMethod函数一样会添加方法;如果类中已存在name指定的方法,则类似于method_setImplementation一样代替原方法的实现。

class_getMethodImplementation函数,该函数在向类实例发送消息时会被调用,并返回一个指向方法实现函数的指针。这个函数比method_getImplementation(class_getInstanceMethod(cls,name))更快。返回的函数指针可能是一个指向runtime内部的函数,而不一定是方法的实际实现。例如,如果类实例无法响应selector,则返回的函数指针将是运行时消息转发机制的一部分。

class_respondsToSelector函数,我们通常使用NSObject类的respondsToSelector:或instancesRespondToSelector:方法来达到相同目的.

八.协议
1.协议相关的操作包含以下函数:

//添加协议
BOOL class_addProtocol(Class cls, Protocol *protocol);

//返回类是否实现指定的协议
BOOL class_conformsToProtocol(Class cls, Protocol *protocol);

//返回类实现的协议列表
Protocol *class_copyProtocolList(Class cls, unsigned int *outCount);

class_conformToProtocol函数可以使用NSObject类的conformToProtocol:方法来替代。
class_copyProtocolList函数返回的是一个数组,在使用后我们需要使用free()手动释放。

九.版本
1.版本相关的操作包含以下函数:

//获取版本号
int class_getVersion(Class cls);

//设置版本号
void class_setVersion(Class cls, int version);

十.其他
runtime还提供了两个函数来供CoreFoundation的tool-free bridging使用,即:
Class objc_getFutureClass(const char *name);
void objc_setFutureClass(Class cls, const char *name);
通常我们不直接使用这两个函数。
代码:
MyClass.h文件

@interface MyClass : NSObject

@property(nonatomic,strong) NSArray *array;

@property(nonatomic,copy) NSString *string;

- (void)method1;

- (void)method2;

+ (void)classMethod1;

@end

MyClass.m文件

#import "MyClass.h"

@interface MyClass (){
    NSInteger _instance1;
    NSString *_instance2;
}

@property(nonatomic,assign) NSUInteger integer;

- (void)method3WithArg1:(NSInteger)arg1 arg2:(NSInteger*)arg2;

@end

@implementation MyClass

+ (void)classMethod1{

}

- (void)method1{
    NSLog(@"call method method1");
}

- (void)method2{

}

- (void)method3WithArg1:(NSInteger)arg1 arg2:(NSInteger*)arg2{
    NSLog(@"arg1:%d,arg2:%@",(int)arg1,(int)arg1);
}

@end

main.m文件

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        MyClass *myClass = [[MyClass alloc] init];
        unsigned int outCount = 0;

        Class cls = myClass.class;

        //类名
        NSLog(@"class name:%s",class_getName(cls));
        NSLog(@"==========================================================");

        //父类
        NSLog(@"super class name:%s",class_getName(class_getSuperclass(cls)));
        NSLog(@"==========================================================");

        //是否是元类
        NSLog(@"MyClass is %@ a meta-class",(class_isMetaClass(cls)?@"":@"not"));
        NSLog(@"==========================================================");

        Class meta_class = objc_getMetaClass(class_getName(cls));
        NSLog(@"%s's meta-class is %s", class_getName(cls), class_getName(meta_class));
        NSLog(@"==========================================================");

        //变量实例大小
        NSLog(@"instance size:%zu",class_getInstanceSize(cls));
        NSLog(@"==========================================================");

        //成员变量
        Ivar *ivars = class_copyIvarList(cls, &outCount);
        for(int i = 0; i < outCount; i++){
            Ivar ivar = ivars[i];
            NSLog(@"instance variable's name: %s at index: %d", ivar_getName(ivar), i);
        }
        free(ivars);

        Ivar string = class_getInstanceVariable(cls, "_string");
        if (string != NULL) {
            NSLog(@"instace variable %s", ivar_getName(string));
        }
        NSLog(@"==========================================================");

        //属性操作
        objc_property_t *properties = class_copyPropertyList(cls, &outCount);
        for (int i = 0; i < outCount; i++) {
            objc_property_t property = properties[i];
            NSLog(@"property's name: %s", property_getName(property));
        }
        free(properties);

        objc_property_t array = class_getProperty(cls, "array");
        if (array != NULL) {
            NSLog(@"property %s", property_getName(array));
        }
        NSLog(@"==========================================================");

        //方法操作
        Method *methods = class_copyMethodList(cls, &outCount);
        for (int i = 0; i < outCount; i++) {
            Method method = methods[i];
            NSLog(@"method's signature:%s",method_getName(method));
        }
        free(methods);

        Method method1 = class_getInstanceMethod(cls, @selector(method1));
        if (method1 != NULL) {
            NSLog(@"method %s",method_getName(method1));
        }

        Method classMethod = class_getClassMethod(cls, @selector(classMethod1));
        if (classMethod != NULL) {
            NSLog(@"method %s",method_getName(classMethod));
        }
        NSLog(@"MyClass is%@ responsd to selector: method3WithArg1:arg2:", class_respondsToSelector(cls, @selector(method3WithArg1:arg2:)) ? @"" : @" not");

        IMP imp = class_getMethodImplementation(cls, @selector(method1));
        imp();
        NSLog(@"==========================================================");

        //协议
        Protocol * __unsafe_unretained * protocols = class_copyProtocolList(cls, &outCount);
        Protocol * protocol;
        for (int i = 0; i < outCount; i++) {
            protocol = protocols[i];
            NSLog(@"protocol name: %s", protocol_getName(protocol));
        }
        NSLog(@"MyClass is%@ responsed to protocol %s", class_conformsToProtocol(cls, protocol) ? @"" : @" not", protocol_getName(protocol));
        NSLog(@"==========================================================");
    }
    return 0;
}

十一.动态创建类
1.动态创建类涉及到以下几个函数:

//创建一个新类和元类
Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes);

//销毁一个类及其相关的类
void objc_disposeClassPair(Class cls);

//在应用中注册由objc_allocateClassPair创建类
void objc_registerClassPair(Class cls);

objc_allocateClassPair函数:如果我们要创建一个根类,则superclass指定为Nil。extraBytes通常指定为0,该参数是分配给类和元类对象尾部的索引ivars的字节数。

为了创建一个新类,我们需要调用objc_allocateClassPair。然后使用诸如class_addMethod,class_addIvar等函数来为新创建的类添加方法、实例变量和属性等。完成这些后,我们需要调用objc_registerClassPair函数来注册类,之后这个新类就可以在程序中使用了。

实例方法和实例变量应该添加到类自身上,而类方法应该添加到类的元类上。

objc_disposeClassPair函数用于销毁一个类,不过需要注意的是,如果程序运行中存在类或其子类的实例,则不能调用针对类调用该方法。
代码:

Class cls = objc_allocateClassPair(MyClass.class, "MySubClass", 0);
class_addMethod(cls, @selector(submethod1), (IMP)imp_submethod1, "v@:");
class_replaceMethod(cls, @selector(method1), (IMP)imp_submethod1, "v@:");
class_addIvar(cls, "_ivar1", sizeof(NSString *), log(sizeof(NSString *)), "i");

objc_property_attribute_t type = {"T", "@\"NSString\""};
objc_property_attribute_t ownership = { "C", "" };
objc_property_attribute_t backingivar = { "V", "_ivar1"};
objc_property_attribute_t attrs[] = {type, ownership, backingivar};

class_addProperty(cls, "property2", attrs, 3);
objc_registerClassPair(cls);

id instance = [[cls alloc] init];
[instance performSelector:@selector(submethod1)];
[instance performSelector:@selector(method1)];

十二.动态创建对象
1.动态创建对象的函数如下:

//创建类实例
id class_createInstance(Class cls, size_t extraBytes);

//在指定位置创建类实例
id objc_constructInstance ( Class cls, void *bytes );

//销毁类实例
void * objc_destructInstance ( id obj );

class_createInstance函数:创建实例时,会在默认的内存区域为类分配内存。extraBytes参数表示分配的额外字节数。这些额外的字节数可用于存储在类定义中所定义的实例变量之外的实例变量。该函数在ARC环境下无法使用。

调用class_createInstance的效果与+alloc方法类似。不过在使用class_createInstance时,我们需要确切的知道我们要用它来做什么。在下面的例子中,我们用NSString来测试一下该函数的实际效果:

id theObject = class_createInstance(NSString.class,sizeof(unsigned));
id str1 = [theObject init];
NSLog(@"%@",[str1 class]);

id str2 = [[NSString alloc] initWithString:@"test"];
NSLog(@"%@",[str2 class]);

使用class_createInstance函数获取的是NSString实例,而不是类簇中的默认占位符类__NSCFConstantString。

objc_constructInstance函数:在指定的位置(bytes)创建类实例。
objc_destructInstance函数:销毁一个类的实例,但不会释放并移除任何与其相关的引用。

十三.实例操作函数
实例操作函数主要是针对我们创建的实例对象的一系列操作函数,我们可以使用这组函数来从实例对象中获取我们想要的一些信息,如实例对象中变量的值。这组函数可以分为三小类:
1.针对整个对象进行操作的函数,这类函数包含:

//返回指定对象的一份拷贝
id object_copy(id obj, size_t size);

//释放指定对象占用的内存
id object_dispose(id obj);

2.针对对象实例变量进行操作的函数,这类函数包含:

//修改类实例的实例变量的值
Ivar object_setInstanceVariable(id obj, const char *name, void *value);

//获取对象实例变量的值
Ivar object_getInstanceVariable(id obj, const char *name, void **outValue);

//返回指向给定对象分配的任何额外字节的指针
void *object_getIndexedIvars(id obj);

//返回对象中实例变量的值
id object_getIvar(id obj,Ivar ivar);

//设置对象中实例变量的值
id object_setIvar(id obj, Ivar ivar, id value)

如果实例变量的Ivar已经知道,那么调用object_getIvar会比object_getInstanceVariable函数快,相同情况下,object_setIvar也比object_setInstanceVariable快。

3.针对对象的类进行操作的函数,这类函数包括:

//返回给定对象的类名
const char *object_getClassName(id obj);

//返回对象的类
Class object_getClass(id obj);

//设置对象的类
Class object_setClass(id obj,Class cls);

十四.获取类定义
objective-c动态运行库会自动注册我们代码中定义的所有的类。我们也可以在运行时创建类定义并使用objc_addClass函数来注册它们。runtime提供了一系列函数来获取类定义相关的信息,这些函数主要包括:

//获取已注册的类定义的列表
int objc_getClassList(Class *buffer, int bufferCount);

//创建并返回一个指向所有已注册的指针列表
Class *objc_copyClassList(unsigned int *outCount);

//返回指定类的类定义
Class objc_lookUpClass(const char *name);
Class objc_getClass(const char *name);
Class objc_getRequiredClass(const char *name);

//返回指定类的元类
Class objc_getMetaClass(const char *name);

objc_getClassList函数:获取已注册的类定义的列表。我们不能假设从该函数中获取的类对象是继承自NSObject体系的,所以在这些类上调用方法时,都应该先检测一下这个方法是否在这个类中实现。
下面代码演示了该函数的用法:

int numClasses;
Class *classes = NULL;

numClasses = objc_getClassList(NULL,0);
if (numClasses > 0){
    classes = malloc(sizeof(Class)*numClasses);
    numClasses = objc_getClassList(classes,numClasses);

    NSLog(@"number of classes:%d",numClasses);
    for(int i=0;i<numClasses;i++){
        Class cls = classes[i];
        NSLog(@"class name:%s",class_getName(cls));
    }
    free(classes);
}

获取类定义的方法有三个:objc_lookUpClass,objc_getClass和objc_getRequiredClass。如果类在运行时未注册,则objc_lookUpClass会返回nil,而objc_getClass会调用类处理回调,并再次确认类是否注册,如果确认未注册,再返回nil。而objc_getRequiredClass函数的操作与objc_getClass相同,只不过如果没有找到类,则会杀死进程。

objc_getMetaClass函数:如果指定的类没有注册,则该函数会调用类处理回调,并再次确认类是否注册,如果确认未注册,再返回nil。不过,每个类定义都必须有一个有效的元类定义,所以这个函数总是返回一个元类定义,不管它是否有效。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值