RunTime之类与对象

我们知道,Objective-C是一门动态语言,它将很多静态语言在编译时期做的事放到了运行时来处理。用C++编写的程序通过编译器直接把函数地址硬编码进入可执行文件;而Objective-C无法通过编译器直接把函数地址硬编码进入可执行文件,而是在程序运行的时候,利用Runtime根据条件判断作出决定,函数标识与函数执行的真正内容之间的关联可以动态修改。这样我们在写代码的时候,声明一个方法,但不对该方法做实现,静态语言在编译时就会报错,但OC中编译时不会报错。这种动态特性给我们带来的好处在于:我们写代码时更具有灵活性,例如我们可以把消息转发给想要的对象,或者交换一个方法的实现等等。

当然,这也意味着OC语言不仅需要一个编译器,还需要一个运行时系统来执行编译的代码。这个运行时系统就像一个操作系统一样,让它所有的工作可以正常运行。这就是我们本篇要聊的Runtime

1.类(Class)

OC的类是由Class类型来表示的。这个Class类型是什么呢?

//objc.h文件
typedef struct objc_class *Class;

//runtime.h文件
struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;  //指向meta-class(元类)

#if !__OBJC2__
    Class super_class                                        OBJC2_UNAVAILABLE;  //父类,如果是根类,则为NULL
    const char *name                                         OBJC2_UNAVAILABLE;  //类名
    long version                                             OBJC2_UNAVAILABLE;  //类的版本信息,默认为0
    long info                                                OBJC2_UNAVAILABLE;  //类信息,供运行期使用的一些位标识
    long instance_size                                       OBJC2_UNAVAILABLE;  //该类的实例变量大小
    struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;  //该类的成员变量链表
    struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;  //该类的方法链表
    //用于缓存最近使用的方法。一个接收者对象接收到一个消息时,它会根据isa指针去查找能够响应这个消息的对象。   //在实际使用中,这个对象只有一部分方法是常用的,很多方法很少用或者根本用不上。这种情况下,如果每次消息来时都是methodLists中遍历一遍,性能势必很差。   //这时,cache就派上用场了。在我们每次调用过一个方法后,这个方法就会被缓存到cache列表中,下次调用的时候runtime就会优先去cache中查找,如果cache没有,才到methodLists中查找方法。   //这样,对于那些经常用到的方法的调用,提高了调用的效率。
    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;  //协议链表
#endif

} OBJC2_UNAVAILABLE;

关于cache,这里用一个示例来解说:

//    第一步:执行[NSString alloc]。由于NSString没有+alloc方法,于是去父类NSObject去查找。
//    第二步:检测NSObject是否响应+alloc方法,发现响应,于是检测NSString类,并根据其所需的内存空间大小开始分配内存空间,然后把isa指针指向NSString类。同时,+alloc也被加进cache列表里面。
//    第三步:执行-init方法,如果NSString响应该方法,则直接将其加入cache;如果不响应,则去父类查找。
//    第四步:如果再以[[NSString alloc] init]这种方式来创建字符串,则会直接从cache中取出相应的方法,直接调用。
    NSString *str = [[NSString alloc] init];

cache是一个指向objc_cache结构体的指针,这个结构体的定义如下:

struct objc_cache {
    //指定分配的缓存bucket的总数
    unsigned int mask /* total = mask + 1 */                 OBJC2_UNAVAILABLE;
    //指定实际占用的缓存bucket的总数
    unsigned int occupied                                    OBJC2_UNAVAILABLE;
    //指向Method数据结构指针的数组
    Method buckets[1]                                        OBJC2_UNAVAILABLE;
};

2.对象

我们知道,OC中有一个id类型,该类型的对象可以转换为任何一种对象。

struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
};

/// A pointer to an instance of a class.
typedef struct objc_object *id;

从定义可以看到,id类型是一个objc_object结构类型的指针。objc_object结构体也只是一个指向其类的isa指针。这样,当我们向一个Objective-C对象发送消息时,运行时库会根据实例对象的isa指针找到这个实例对象所属的类。runtime库会在类的方法列表及父类的方法列表中去寻找与消息对应的selector指向的方法,找到后运行这个方法。

当创建一个特定类的实例对象时,分配的内存包含一个objc_object数据结构,然后是类的实例变量的数据。NSObject类的alloc和allocWithZone:方法使用函数class_createInstance来创建objc_object数据结构。

3.meta-class(元类)

所有的类本身也是一个对象,我们可以向这个对象发送消息(即调用类方法)。如:

[NSString string];

从上面的代码可以看到,+string消息发送给了NSString类,而这个NSString也是一个对象。既然是对象,那么它也是一个objc_object指针,它包含一个指向其类的一个isa指针。那么这个isa指针指向哪里呢?为了调用+string方法,这个类的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指针是指向它自己。

下图描述了类及相应meta-class类的继承关系:

【注意】:所有meta-class中isa指针都指向根meta-class。而根meta-class则指向自身。根meta-class是通过继承Root class产生的。与Root class结构体成员一致,不同的是根meta-class的isa指针指向自身。

对于NSObject继承体系来说,其实例方法对体系中的所有实例、类和meta-class都是有效的;而类方法对于体系内的所有类和meta-class都是有效的。

下面来看一个示例:

- (void)ex_registerClassPair {
    //创建存储空间
    Class newClass = objc_allocateClassPair([NSObject class], "GofClass", 0);

    /**
     动态添加方法

     @param cls 类类型
     @param name 选择器(SEL)
     @param imp 函数指针
     @param type 方法类型
     */
    SuppressUndeclaredSelectorWarning(class_addMethod(newClass, @selector(testMetaClass), (IMP)TestMetaClass, "[email protected]:"));

    //注册这个类
    objc_registerClassPair(newClass);
    id instance = [[newClass alloc] init];
    SuppressUndeclaredSelectorWarning([instance performSelector:@selector(testMetaClass)]);
}

//隐式参数
void TestMetaClass(id self, SEL _cmd) {
    NSLog(@"This object is %p", self);
    NSLog(@"Class is %@, super class is %@", [self class], [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]));
}

运行之后,打印结果如下:

在for循环中,通过objc_getClass来获取对象的isa,并将其打印出来,依此一直回溯到NSObject的meta-class。分析打印结果,可以看到最后指针指向的地址是0x0,即NSObject的meta-class的类地址。

4.类相关函数

runtime提供了大量的函数来操作类和对象。类的操作方法大部分是以class_为前缀的,而对象的操作方法大部分是以objc_object_为前缀。

4.1获取类名

    //1.获取类名
    NSLog(@"获取类名:%s", class_getName([GofBaseViewController class]));

【注意】:如果传入的cls为Nil/NULL/nil,则返回nil。

4.2获取父类

    //2.获取父类
    NSLog(@"获取父类:%@", class_getSuperclass([GofBaseViewController class]));
    NSLog(@"获取父类2:%@", [GofBaseViewController superclass]);

【注意】:如果传入的cls为Nil/NULL/nil或者cls为根类时,则返回nil。

4.3是否meta-class

    //3.是否meta-class
    NSObject *obj = [[NSObject alloc] init];
    Class objectClsObj = object_getClass(obj);
    Class objectClsObj_isa = object_getClass(objectClsObj);
    NSLog(@"是否meta-class:%d", class_isMetaClass(objectClsObj));  //否
    NSLog(@"是否meta-class:%d", class_isMetaClass(objectClsObj_isa));  //是

【注意】:如果传入的cls为meta-class,则返回YES;否则返回NO;

4.4实例变量大小

    //4.获取实例变量大小
    NSLog(@"获取实例变量大小:%zu", class_getInstanceSize(objectClsObj));

4.5成员变量

主要有以下相关函数:

//获取类中指定名称实例成员变量的信息
OBJC_EXPORT Ivar class_getInstanceVariable(Class cls, const char *name)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
//获取类成员变量的信息
OBJC_EXPORT Ivar class_getClassVariable(Class cls, const char *name)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
//添加成员变量
OBJC_EXPORT BOOL class_addIvar(Class cls, const char *name, size_t size,
                               uint8_t alignment, const char *types)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
//获取整个成员变量列表
OBJC_EXPORT Ivar *class_copyIvarList(Class cls, unsigned int *outCount)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);

下面通过一个示例细看一下:

GofPerson.h文件
@interface GofPerson : NSObject
{
    NSInteger       age;
}

@property (nonatomic, strong) NSString *name;  //!<姓名

@end

GofPerson.m文件
@interface GofPerson ()

@property (nonatomic, strong) NSString *phone;

@end

@implementation GofPerson

@end

    GofPerson *person = [[GofPerson alloc] init];
    Class objectClsObj = object_getClass(person);

    Ivar ivar0 = class_getInstanceVariable(objectClsObj, "age");
    const char *age = ivar_getName(ivar0);
    NSLog(@"成员变量:%s", age);

    Ivar ivar1 = class_getClassVariable([GofPerson class], "isa");
    const char *classVariable = ivar_getName(ivar1);
    NSLog(@"成员变量:%s", classVariable);

    //在这里添加无效
    class_addIvar(objectClsObj, "name1", sizeof(id), log2(sizeof(id)), "@");

    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList([GofPerson class], &count);

    for (int i = 0; i < count; i++) {
        Ivar ivar = ivars[i];
        const char *name = ivar_getName(ivar);

        NSLog(@"成员变量:%s", name);
    }

【说明】:class_addIvar方法只能在objc_allocateClassPair函数与objc_registerClassPair之间调用。

打印结果如下:

4.6属性

和上一小节一样,我们先看看主要都有哪些函数?

//获取指定的属性
OBJC_EXPORT objc_property_t class_getProperty(Class cls, const char *name)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
//获取属性列表
OBJC_EXPORT objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
//添加属性
OBJC_EXPORT BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)
    OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0);
//替换类的属性
OBJC_EXPORT void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)
    OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0);

怎么使用呢?

    GofPerson *person = [[GofPerson alloc] init];
    Class objectClsObj = object_getClass(person);

    //获取指定的属性
    objc_property_t property0 = class_getProperty(objectClsObj, "phone");
    const char *phone = property_getName(property0);
    NSLog(@"成员属性:%s", phone);

    //添加属性
    objc_property_attribute_t attribute1 = {"T", "@\"NSString\""};  //type
    objc_property_attribute_t attribute2 = {"C", ""};  //copy
    objc_property_attribute_t attribute3 = {"N", ""};  //nonatomic
    objc_property_attribute_t attribute4 = {"V", "_email"};  //variable name
    objc_property_attribute_t attributesList[] = {attribute1, attribute2, attribute3, attribute4};
    if(class_addProperty([GofPerson class], "email", attributesList, 4)) {
        NSLog(@"add property success!");
    }
    else {
        NSLog(@"add property failure!");
    }

    //替换属性的信息(如果没有原属性会新建一个属性)
    objc_property_attribute_t attribute11 = {"T", "@\"NSString\""};  //type
    objc_property_attribute_t attribute22 = {"C", ""};  //copy
    objc_property_attribute_t attribute33 = {"N", ""};  //nonatomic
    objc_property_attribute_t attribute44 = {"V", "_mobile"};  //variable name
    objc_property_attribute_t attributesList1[] = {attribute11, attribute22, attribute33, attribute44};
    class_replaceProperty([GofPerson class], "mobile", attributesList1, 4);

    //获取属性列表(分类中的属性也会显示)
    unsigned int count = 0;
    objc_property_t *propertys = class_copyPropertyList([GofPerson class], &count);

    for (int i = 0; i < count; i++) {
        objc_property_t property = propertys[i];
        const char *name = property_getName(property);
        const char *attribute = property_getAttributes(property);
        NSLog(@"propertyName: %s, attribute: %s", name, attribute);

        unsigned int attributeCount;
        objc_property_attribute_t *attributeList = property_copyAttributeList(property, &attributeCount);
        for (unsigned int j = 0; j < attributeCount; j++) {
            objc_property_attribute_t attribute = attributeList[j];
            const char *name = attribute.name;
            const char *value = attribute.value;
            NSLog(@"attribute name: %s, value: %s", name, value);
        }
    }
    free(propertys);

打印结果如下:

4.7方法

主要有以下函数来做方法操作:

//添加方法
OBJC_EXPORT BOOL class_addMethod(Class cls, SEL name, IMP imp,
                                 const char *types)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
//获取实例方法
OBJC_EXPORT Method class_getInstanceMethod(Class cls, SEL name)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
//获取类方法
OBJC_EXPORT Method class_getClassMethod(Class cls, SEL name)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
//获取所有方法的数组
OBJC_EXPORT Method *class_copyMethodList(Class cls, unsigned int *outCount)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
//替代方法的实现
OBJC_EXPORT IMP class_replaceMethod(Class cls, SEL name, IMP imp,
                                    const char *types)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
//返回方法的具体实现
OBJC_EXPORT IMP class_getMethodImplementation(Class cls, SEL name)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
OBJC_EXPORT IMP class_getMethodImplementation_stret(Class cls, SEL name)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0)
    OBJC_ARM64_UNAVAILABLE;
//类实例是否响应指定的selector
OBJC_EXPORT BOOL class_respondsToSelector(Class cls, SEL sel)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);

我们先给上面定义的GofPerson类添加几个方法:

//GofPerson.h
@interface GofPerson : NSObject
{
    NSInteger       age;
}

@property (nonatomic, strong) NSString *name;  //!<姓名

+ (GofPerson *)createPersonWithName:(NSString *)nameString
                                age:(NSInteger)ageInt;

- (void)setName:(NSString *)nameString
            age:(NSInteger)ageInt;

@end

//GofPerson.m
@interface GofPerson ()

@property (nonatomic, strong) NSString *phone;

@end

@implementation GofPerson

+ (GofPerson *)createPersonWithName:(NSString *)nameString
                                age:(NSInteger)ageInt
{
    GofPerson *person = [[GofPerson alloc] init];
    [person setName:@"LeeGof" age:18 phone:@"13800138000"];
    return person;
}

- (void)setName:(NSString *)nameString
            age:(NSInteger)ageInt
{
    self.name = nameString;
    age = ageInt;
}

- (void)setName:(NSString *)nameString
            age:(NSInteger)ageInt
          phone:(NSString *)phoneString
{
    self.name = nameString;
    age = ageInt;
    self.phone = phoneString;
}

- (void)dealloc {
    NSLog(@"GofPerson dealloc");
}

@end

然后我们来对上面的方法操作函数做一个简单的示例:

/**
 操作方法
 */
- (void)operateMethod
{
    GofPerson *person = [[GofPerson alloc] init];
    Class objectClsObj = object_getClass(person);
    Class objectClsObj_isa = object_getClass(objectClsObj);

    //添加方法
    SuppressUndeclaredSelectorWarning(class_addMethod(objectClsObj, @selector(newMethod), (IMP)testNewMethod, "[email protected]:"));
    //调用新添加的方法
    SuppressUndeclaredSelectorWarning([person performSelector:@selector(newMethod)]);

    //获取实例方法,如果方法不存在,返回nil
    Method instanceMethod = class_getInstanceMethod(objectClsObj, @selector(setName:age:));
    NSLog(@"instanceMethod:%s", sel_getName(method_getName(instanceMethod)));

    //获取类方法
    Method classMethod = class_getClassMethod(objectClsObj, @selector(createPersonWithName:age:));
    NSLog(@"classMethod:%s", sel_getName(method_getName(classMethod)));

    //获取实例方法列表
    unsigned int count = 0;
    Method *methods = class_copyMethodList(objectClsObj, &count);
    for (int i = 0; i < count; i++) {
        Method methodItem = methods[i];
        const char *methodType = method_getTypeEncoding(methodItem);// 获取方法参数类型和返回类型
        NSLog(@"instance method item%d:%s %s",i, sel_getName(method_getName(methodItem)), methodType);
    }
    free(methods);
    //获取类方法列表
    unsigned int countClass = 0;
    Method *classMethods = class_copyMethodList(objectClsObj_isa, &countClass);
    for (int i = 0; i < countClass; i++) {
        Method methodItem = classMethods[i];
        const char *methodType = method_getTypeEncoding(methodItem);// 获取方法参数类型和返回类型
        NSLog(@"class method item%d:%s %s",i, sel_getName(method_getName(methodItem)), methodType);
    }
    free(classMethods);
    //替换方法实现 如果类中不存在newMethod指定的方法,则会和class_addMethod函数一样添加方法
    SuppressUndeclaredSelectorWarning(class_replaceMethod(objectClsObj, @selector(newMethod), (IMP)replaceNewMethod, "[email protected]:"));
    //调用替换的方法
    SuppressUndeclaredSelectorWarning([person performSelector:@selector(newMethod)]);

    //获取方法的具体实现. 该函数在向类实例发送消息时会被调用,并返回一个指向方法实现函数的指针。返回的函数指针可能是一个指向runtime内部的函数,而不一定是方法的实际实现。    //例如,如果类实例无法响应selector,则返回的函数指针将是运行时消息转发机制的一部分。
    IMP implement = class_getMethodImplementation(objectClsObj, @selector(setName:age:));
    //获取方法的具体实现.该方法的返回值类型为struct
    IMP implement1 = class_getMethodImplementation_stret(objectClsObj, @selector(setName:age:));

    //判断类实例是否响应指定的selector
    BOOL canResponse = class_respondsToSelector(objectClsObj, @selector(setName:age:));

    NSLog(@"类实例是否响应 : %d", canResponse);
}

//隐式参数
void testNewMethod(id self, SEL _cmd) {
    NSLog(@"Hello,newMethod");
}

void replaceNewMethod(id self, SEL _cmd) {
    NSLog(@"Hello,replaceNewMethod");
}

看一下打印结果:

4.8协议

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

//类是否实现指定的协议.可以使用NSObject类的conformsToProtocol:方法来代替
OBJC_EXPORT BOOL class_conformsToProtocol(Class cls, Protocol *protocol)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
//添加协议
OBJC_EXPORT BOOL class_addProtocol(Class cls, Protocol *protocol)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
//返回类实现的协议列表
OBJC_EXPORT Protocol * __unsafe_unretained *class_copyProtocolList(Class cls, unsigned int *outCount)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);

看一个简单的示例:

    GofPerson *person = [[GofPerson alloc] init];
    Class objectClsObj = object_getClass(person);

    Protocol *coding = objc_getProtocol("NSCoding");

    //类是否实现了协议
    BOOL isImplement = class_conformsToProtocol(objectClsObj, coding);
    NSLog(@"类是否实现了协议: %d", isImplement);

    //添加协议
    BOOL isSuccess = class_addProtocol(objectClsObj, coding);
    NSLog(@"添加协议是否成功:%d", isSuccess);

    //类是否实现了协议
    BOOL isImplement1 = class_conformsToProtocol(objectClsObj, coding);
    NSLog(@"类是否实现了协议: %d", isImplement1);

    unsigned int count = 0;
    Protocol * __unsafe_unretained *protocols = class_copyProtocolList(objectClsObj, &count);

    for (int i = 0; i < count; i++) {
        Protocol *item = protocols[i];
        NSLog(@"protocol item%d:%s",i, protocol_getName(item));
    }    free(protocols);

打印结果:

4.9版本

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

//获取版本号
OBJC_EXPORT int class_getVersion(Class cls)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
//设置版本号
OBJC_EXPORT void class_setVersion(Class cls, int version)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);

这两个函数比较简单,示例如下:

    //获取版本
    int version = class_getVersion([GofPerson class]);
    NSLog(@"版本:%d", version);

    //设置版本
    class_setVersion([GofPerson class], 1);

4.10动态创建类

动态创建类包含以下函数:

// 创建新类并分配存储空间
OBJC_EXPORT Class objc_allocateClassPair(Class superclass, const char *name,
                                         size_t extraBytes)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
//销毁一个类
OBJC_EXPORT void objc_disposeClassPair(Class cls)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
//在应用中注册由objc_allocateClassPair创建的类
OBJC_EXPORT void objc_registerClassPair(Class cls)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);

使用在前面已经写过,这里温习一下:

- (void)createClass
{
    //创建新类  如果是创建基类,那么superClass指定为Nil
    Class newClass = objc_allocateClassPair([GofBaseViewController class], "GofClass", 0);

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

     @param cls 类类型
     @param name 选择器(SEL)
     @param imp 函数指针
     @param type 方法类型
     */
    SuppressUndeclaredSelectorWarning(class_addMethod(newClass, @selector(newMethod), (IMP)newMethodImplement, "[email protected]:"));
    //在应用中注册这个类 如果已经注册,再注册会直接崩溃!!!
    objc_registerClassPair(newClass);

    id instance = [[newClass alloc] init];
//    id instance = class_createInstance(newClass, 0);  //ARC下不能使用
    SuppressUndeclaredSelectorWarning([instance performSelector:@selector(newMethod)]);
}

//隐式参数
void newMethodImplement(id self, SEL _cmd) {
    NSLog(@"newMethodImplement");
}

- (void)dealloc
{
    Class newClass = NSClassFromString(@"GofClass");
    if (newClass) {
        objc_disposeClassPair(newClass);  //如果程序运行中还存在类或者其子类的实例时,则不能调用该方法
    }
}

4.11对象操作

包含以下函数:

//MRC 创建对象
OBJC_EXPORT id class_createInstance(Class cls, size_t extraBytes)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
    OBJC_ARC_UNAVAILABLE;
//MRC 销毁对象
OBJC_EXPORT void *objc_destructInstance(id obj)
    OBJC_AVAILABLE(10.6, 3.0, 9.0, 1.0)
    OBJC_ARC_UNAVAILABLE;
//MRC 对象拷贝
OBJC_EXPORT id object_copy(id obj, size_t size)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
    OBJC_ARC_UNAVAILABLE;
//MRC 释放指定对象占用的内存
OBJC_EXPORT id object_dispose(id obj)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
    OBJC_ARC_UNAVAILABLE;
//MRC 修改类实例的实例变量的值
OBJC_EXPORT Ivar object_setInstanceVariable(id obj, const char *name, void *value)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
    OBJC_ARC_UNAVAILABLE;
//MRC 获取对象实例变量的值
OBJC_EXPORT Ivar object_getInstanceVariable(id obj, const char *name, void **outValue)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
    OBJC_ARC_UNAVAILABLE;
//ARC 返回对象中实例变量的值
OBJC_EXPORT id object_getIvar(id obj, Ivar ivar)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
//ARC 设置对象中实例变量的值
OBJC_EXPORT void object_setIvar(id obj, Ivar ivar, id value)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
//返回给定对象的类名(objc.h)
OBJC_EXPORT const char *object_getClassName(id obj)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
//返回对象的类(runtime.h)
OBJC_EXPORT Class object_getClass(id obj)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);//设置对象的类OBJC_EXPORT Class object_setClass(id obj, Class cls)    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);

示例如下:

    //MRC下操作
    //动态创建对象
    id object = class_createInstance([NSObject class], 0);
    //MRC 拷贝对象
    //常用于这样的场景:有类A和类B,且类B是类A的子类。类B通过添加一些额外的属性来扩展类A。    //现在我们创建了一个A类的实例对象,并希望在运行时将这个对象转换为B类的实例对象,这样可以添加数据到B类的属性中。    //这种情况下,我们没有办法直接转换,因为B类的实例会比A类的实例更大,没有足够的空间来放置对象。
    id person = object_copy(object, class_getInstanceSize(GofPerson.class));    //设置对象的类
    object_setClass(person, GofPerson.class);
    //MRC 释放对象占用的内存
    object_dispose(object);

    //MRC 修改实例变量的值
    object_setInstanceVariable(person, "_name", @"bbb");
    void *outValue = (void *)0x1;
    //MRC 获取实例变量的值
    object_getInstanceVariable(person, "_name", &outValue);
    if (nil == outValue) {
        NSLog(@"nil");
    }
    else
    {
        NSLog(@"outValue:%@", outValue);
    }
    if (person) {
        [person release];
        person = nil;
    }

    //ARC下操作
    GofPerson *person = [[GofPerson alloc] init];
    Class objectClsObj = object_getClass(person);

    //ARC 获取实例变量的值
    Ivar ivar = class_getInstanceVariable(objectClsObj, "_phone");
    //ARC 修改实例变量的值
    object_setIvar(person, ivar, @"13900000000");
    NSLog(@"getIvar:%@", object_getIvar(person, ivar));

    //获取类名
    NSLog(@"类名:%s", object_getClassName(person));

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

4.12获取类定义

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

//MRC 获取已注册的类定义的列表
OBJC_EXPORT int objc_getClassList(Class *buffer, int bufferCount)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
//创建并返回一个指向所有已注册类的指针列表
OBJC_EXPORT Class *objc_copyClassList(unsigned int *outCount)
    OBJC_AVAILABLE(10.7, 3.1, 9.0, 1.0);
//返回指定类的类定义 //如果类在运行时未注册,则objc_lookUpClass会返回nil,而objc_getClass会调用类处理回调,并再次确认该类是否注册,如果确认未注册,则返回nil。//而objc_getRequiredClass函数的操作与objc_getClass相同,只不过如果没有找到类,则会杀死进程OBJC_EXPORT Class objc_lookUpClass(const char *name)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
OBJC_EXPORT Class objc_getClass(const char *name)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
OBJC_EXPORT Class objc_getRequiredClass(const char *name)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
//返回指定类的元类
OBJC_EXPORT Class objc_getMetaClass(const char *name)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);

示例如下:

    //MRC
    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);
    }

    //ARC
    unsigned int count = 0;
    Class *classes = objc_copyClassList(&count);
    NSLog(@"number of classes: %d", count);
    for (int i = 0; i < count; i++) {
        Class class = classes[i];
        NSLog(@"class name: %s", class_getName(class));
    }

5.小结

本篇介绍了运行时类与对象的相关数据结构,并对与之相关的函数做了一个简单的领读和演示。通过这些函数,我们可以灵活的运用运行时特性进行开发,比如动态的添加类、属性、成员变量、方法、协议等等。

时间: 2024-10-29 19:09:38

RunTime之类与对象的相关文章

福利-&gt;KVC+Runtime获取类/对象的属性/成员变量/方法/协议并实现字典转模型

我们知道,KVC+Runtime可以做非常多的事情.有了这个,我们可以实现很多的效果. 这里来个福利,利用KVC+Runtime获取类/对象的所有成员变量.属性.方法及协议: 并利用它来实现字典转模型. 废话不多说,直接上代码: 1.工具类(其实就是NSObject的一个分类)头文件 1 #import <Foundation/Foundation.h> 2 3 @interface NSObject (YSRuntime) 4 5 /** 6 返回当前类的属性数组 7 8 @return 属

runtime 01-类与对象

关于“runtime机制”的问题,要深入理解runtime,首先要从最基本的类与对象开始,本文将详细讲解OC中类与对象的结构层次,后续将逐渐更新如何利用runtime操作类.首先,我们从/usr/include/objc/objc.h 和 runtime.h 中找到对 class 与 object 的定义: 1 // An opaque type that represents an Objective-C class. 2 typedef struct objc_class *Class; 3

利用runtime动态生成对象?

利用runtime我们能够动态生成对象.属性.方法这特性 假定我们要动态生成DYViewController,并为它创建属性propertyName 1)对象名 NSString *class = @"DYViewController"; const char *className = [class cStringUsingEncoding:NSASCIIStringEncoding]; 2)从一个字符串返回一个Class Class newClass = objc_getClass(

JAVA调用系统命令或可执行程序--返回一个Runtime运行时对象,然后启动另外一个进程来执行命令

通过 java.lang.Runtime 类可以方便的调用操作系统命令,或者一个可执行程序,下面的小例子我在windows和linux分别测试过,都通过.基本原理是,首先通过 Runtime.getRuntime() 返回与当前 Java 应用程序相关的运行时对象,然后调用run.exec(cmd)  另启一个进程来执行命令(cmd为要执行的命令). 一.运行一个可执行程序 执行一个.exe的文件,或通过已安装的软件打开一个特定格式的文件,如word.chm或mp3等等. 1. 在window下

Runtime之成员变量&amp;属性&amp;关联对象

上篇介绍了Runtime类和对象的相关知识点,在4.5和4.6小节,也介绍了成员变量和属性的一些方法应用.本篇将讨论实现细节的相关内容. 在讨论之前,我们先来介绍一个很冷僻但又很有用的一个关键字:@encode 1.类型编码 为了协助运行时系统,编译器用字符串为每个方法的返回值.参数类型和方法选择器编码,使用的编码方案在其他情况下也很有用.在 Objective-C 运行时的消息发送机制中,传递参数时,由于类型信息的缺失,需要类型编码进行辅助以保证类型信息也能够被传递.在实际的应用开发中,使用案

iOS runtime实战应用:关联对象

在开始之前建议先阅读iOS runtime的基础理解篇:iOS内功篇:runtime 有筒子在面试的时候,遇到这样一个问题:"如何給NSArray添加一个属性(不能使用继承)",筒子立马蒙逼了,不能用继承,难道用分类?但是分类貌似只能添加方法不能添加属性啊,筒子百思不得其解,直到后来接触到了runtime才恍然大悟. 什么是关联对象 关联对象是指某个OC对象通过一个唯一的key连接到一个类的实例上.举个例子:xiaoming是Person类的一个实例,他的dog(一个OC对象)通过一根

iOS开发笔记之Runtime实用总结

前言 runtime的资料网上有很多了,部分有些晦涩难懂,我通过自己的学习方法总结一遍,主要讲一些常用的方法功能,以实用为主,我觉得用到印象才是最深刻的.另外runtime的知识还有很多,想要了解更多可以一些翻译的官方文档(有点枯燥) 什么是runtime? runtime 是 OC底层的一套C语言的API(引入 <objc/runtime.h> 或<objc/message.h>),编译器最终都会将OC代码转化为运行时代码,通过终端命令编译.m 文件:clang -rewrite

runtime之玩转成员变量

前言: 不铺垫那么多,单刀直入吧:runtime是一个C和汇编写的动态库,就像是一个小小的系统,将OC和C紧密关联在一次,这个系统主要做两件事情. 1,封装C语言的结构体和函数,让开发者在运行时创建,检查或者修改类,对象和方法等2,传递消息,找出方法的最终执行代码 也就是说我们写的OC代码在运行的时候都会转为运行时代码 通过runtime的学习能够更好理解OC的这种消息发送机制,并且我也认为对runtime的学习是对深入学习iOS必不可少的坎,比如你有可能通过阅读一些第三方框架来提高自己的编程技

runtime运行机制

这篇文章主要介绍的是runtime是什么以及怎么用!希望对读者有所帮助! 第一个问题, 1>runtime实现的机制是什么,怎么用,一般用于干嘛? runtime是一套比较底层的纯C语言API, 属于1个C语言库, 包含了很多底层的C语言API. 在我们平时编写的OC代码中, 程序运行过程时, 其实最终都是转成了runtime的C语言代码, runtime算是OC的幕后工作者 比如说,下面一个创建对象的方法中, 对比举例: OC : [[MJPerson alloc] init] runtime