OC对象的分类实质

栏目: IOS · 发布时间: 6年前

内容简介:面试题:OC的类信息存放在哪里。 面试题:对象的isa指针指向哪里。示例代码OC的类信息存放在哪里OC对象主要可以分为三种

面试题:OC的类信息存放在哪里。 面试题:对象的isa指针指向哪里。

示例代码

#import <Foundation/Foundation.h>
#import <objc/runtime.h>

/* Person */ 
@interface Person : NSObject <NSCopying>
{
    @public
    int _age;
}
@property (nonatomic, assign) int height;
- (void)personMethod;
+ (void)personClassMethod;
@end

@implementation Person
- (void)personMethod {}
+ (void)personClassMethod {}
@end

/* Student */
@interface Student : Person <NSCoding>
{
    @public
    int _no;
}
@property (nonatomic, assign) int score;
- (void)studentMethod;
+ (void)studentClassMethod;
@end

@implementation Student
- (void)studentMethod {}
+ (void)studentClassMethod {}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {      
        NSObject *object1 = [[NSObject alloc] init];
        NSObject *object2 = [[NSObject alloc] init];

        Student *stu = [[Student alloc] init];
        [Student load];

        Person *p1 = [[Person alloc] init];
        p1->_age = 10;
        [p1 personMethod];
        [Person personClassMethod];
        Person *p2 = [[Person alloc] init];
        p2->_age = 20;
    }
    return 0;
}

复制代码

OC的类信息存放在哪里OC对象主要可以分为三种

  • instance对象(实例对象)
  • class对象(类对象)
  • meta-class对象(元类对象)

instance对象就是通过类alloc出来的对象,每次调用alloc都会产生新的instance对象

NSObjcet *object1 = [[NSObjcet alloc] init];
NSObjcet *object2 = [[NSObjcet alloc] init];
复制代码

object1和object2都是NSObject的instace对象(实例对象),但他们是不同的两个对象,并且分别占据着两块不同的内存。 instance对象在内存中存储的信息包括

  • isa指针
  • 其他成员变量
OC对象的分类实质

衍生问题:在上图实例对象中根本没有看到方法,那么实例对象的方法的代码放在什么地方呢?那么类的方法的信息,协议的信息,属性的信息都存放在什么地方呢?

class对象

我们通过class方法或runtime方法得到一个class对象。class对象也就是类对象

Class objectClass1 = [object1 class];
Class objectClass2 = [object2 class];
Class objectClass3 = [NSObject class];

// runtime
Class objectClass4 = object_getClass(object1);
Class objectClass5 = object_getClass(object2);
NSLog(@"%p %p %p %p %p", objectClass1, objectClass2, objectClass3, objectClass4, objectClass5);
复制代码

每一个类在内存中有且只有一个class对象。可以通过打印内存地址证明

class对象在内存中存储的信息主要包括

  • isa指针
  • superclass指针
  • 类的属性信息(@property),类的成员变量信息(ivar)
  • 类的对象方法信息(instance method),类的协议信息(protocol)
OC对象的分类实质

成员变量的值时存储在实例对象中的,因为只有当我们创建实例对象的时候才为成员变赋值。但是成员变量叫什么名字,是什么类型,只需要有一份就可以了。所以存储在class对象中。

类方法放在那里?

元类对象 meta-class

//runtime中传入类对象此时得到的就是元类对象
Class objectMetaClass = object_getClass([NSObject class]);
// 而调用类对象的class方法时得到还是类对象,无论调用多少次都是类对象
Class cls = [[NSObject class] class];
Class objectClass3 = [NSObject class];
class_isMetaClass(objectMetaClass) // 判断该对象是否为元类对象
NSLog(@"%p %p %p", objectMetaClass, objectClass3, cls); // 后面两个地址相同,说明多次调用class得到的还是类对象
复制代码

每个类在内存中有且只有一个meta-class对象。 meta-class对象和class对象的内存结构是一样的,但是用途不一样,在内存中存储的信息主要包括

  • isa指针
  • superclass指针
  • 类的类方法的信息(class method)
OC对象的分类实质

meta-class对象和class对象的内存结构是一样的,所以meta-class中也有类的属性信息,类的对象方法信息等成员变量,但是其中的值可能是空的。

对象的isa指针指向哪里

  • 当对象调用实例方法的时候,我们上面讲到,实例方法信息是存储在class类对象中的,那么要想找到实例方法,就必须找到class类对象,那么此时isa的作用就来了。

    [stu studentMethod];
    复制代码

    instance的isa指向class,当调用对象方法时,通过instance的isa找到class,最后找到对象方法的实现进行调用。

  • 当类对象调用类方法的时候,同上,类方法是存储在meta-class元类对象中的。那么要找到类方法,就需要找到meta-class元类对象,而class类对象的isa指针就指向元类对象

    [Student studentClassMethod];
    复制代码

    class的isa指向meta-class当调用类方法时,通过class的isa找到meta-class,最后找到类方法的实现进行调用

    OC对象的分类实质
  • 当对象调用其父类对象方法的时候,又是怎么找到父类对象方法的呢?,此时就需要使用到class类对象superclass指针。

    [stu personMethod];
    [stu init];
    复制代码

    当Student的instance对象要调用Person的对象方法时,会先通过isa找到Student的class,然后通过superclass找到Person的class,最后找到对象方法的实现进行调用,同样如果Person发现自己没有响应的对象方法,又会通过Person的superclass指针找到NSObject的class对象,去寻找响应的方法

    OC对象的分类实质
  • 当类对象调用父类的类方法时,就需要先通过isa指针找到meta-class,然后通过superclass去寻找响应的方法

    [Student personClassMethod];
    [Student load];
    复制代码

    当Student的class要调用Person的类方法时,会先通过isa找到Student的meta-class,然后通过superclass找到Person的meta-class,最后找到类方法的实现进行调用

    最后又是这张静定的isa指向图,经过上面的分析我们在来看这张图,就显得清晰明了很多

    OC对象的分类实质

对isa、superclass总结

  • instance的isa指向class
  • class的isa指向meta-class
  • meta-class的isa指向基类的meta-class,基类的isa指向自己
  • class的superclass指向父类的class,如果没有父类,superclass指针为nil
  • meta-class的superclass指向父类的meta-class,基类的meta-class的superclass指向基类的class
  • instance调用对象方法的轨迹,isa找到class,方法不存在,就通过superclass找父类
  • class调用类方法的轨迹,isa找meta-class,方法不存在,就通过superclass找父类

#####如何证明isa指针的指向真的如上面所说? 我们通过如下代码证明:

NSObject *object = [[NSObject alloc] init];
Class objectClass = [NSObject class];
Class objectMetaClass = object_getClass([NSObject class]);
        
NSLog(@"%p %p %p", object, objectClass, objectMetaClass);
复制代码

打断点并通过控制台打印相应对象的isa指针

OC对象的分类实质

我们发现object->isa与objectClass的地址不同,这是因为从64bit开始,isa需要进行一次位运算,才能计算出真实地址。而位运算的值我们可以通过下载objc源代码找到。

OC对象的分类实质

我们通过位运算进行验证。

OC对象的分类实质

我们发现,object-isa指针地址0x001dffff96537141经过同0x00007ffffffffff8位运算,得出objectClass的地址0x00007fff96537140 接着我们来验证class对象的isa指针是否同样需要位运算计算出meta-class对象的地址。 当我们以同样的方式打印objectClass->isa指针时,发现无法打印

OC对象的分类实质

同时也发现左边objectClass对象中并没有isa指针。我们来到Class内部看一下

typedef struct objc_class *Class;

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */
复制代码

相信了解过isa指针的同学对objc_class结构体内的内容很熟悉了,今天这里不深入研究,我们只看第一个对象是一个isa指针,为了拿到isa指针的地址,我们自己创建一个同样的结构体并通过强制转化拿到isa指针。

struct xx_cc_objc_class{
    Class isa;
};


Class objectClass = [NSObject class];
struct xx_cc_objc_class *objectClass2 = (__bridge struct xx_cc_objc_class *)(objectClass);
复制代码

此时我们重新验证一下

OC对象的分类实质

确实,objectClass2的isa指针经过位运算之后的地址是meta-class的地址。

本文面试题总结:

一个NSObject对象占用多少内存? 答:一个指针变量所占用的大小(64bit占8个字节,32bit占4个字节)

对象的isa指针指向哪里? 答:instance对象的isa指针指向class对象,class对象的isa指针指向meta-class对象,meta-class对象的isa指针指向基类的meta-class对象,基类自己的isa指针也指向自己。

OC的类信息存放在哪里? 答:成员变量的具体值存放在instance对象。对象方法,协议,属性,成员变量信息存放在class对象。类方法信息存放在meta-class对象。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

PHP Cookbook

PHP Cookbook

Adam Trachtenberg、David Sklar / O'Reilly Media / 2006-08-01 / USD 44.99

When it comes to creating dynamic web sites, the open source PHP language is red-hot property: used on more than 20 million web sites today, PHP is now more popular than Microsoft's ASP.NET technology......一起来看看 《PHP Cookbook》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

MD5 加密
MD5 加密

MD5 加密工具