iOS-Developer-OCiOS - Developer - OC 进阶大全

Object-C中类与对象的本质

2019-08-01  本文已影响14人  zwwuchn

通过几道面试题我们来逐渐解开类与对象的本质。

 1. 一个NSObject对象占用多少内存?
 2. 对象isa指针指向哪里?
 3. 什么是class、metaClass对象?
 4. OC的类信息存放在哪里?

Object-C的本质

我们平时编写的Objective-C(后面简称OC)代码,其实底层都是C/C++代码

图片.png

我们来将OC代码转换为C/C++代码

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc 文件名 -o 输出的CPP文件
例如:xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp

@interface LPPerson : NSObject
{
    @public
    int _age;
    NSString *_name;
}
@end

@implementation LPPerson

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        LPPerson *person = [[LPPerson alloc] init];
        person->_age = 18;
        person->_name = @"LP";
    }
    return 0;
}

我们来看一下源码

struct NSObject_IMPL {
    Class isa; 
};

struct LPPerson_IMPL {
    struct NSObject_IMPL NSObject_IVARS; 
    int _age;
    NSString *_name;
};
...
...
图片.png
其实Class在runtime源码中定义 typedef struct objc_class *Class; 稍后我们会分析源码
所以,OC的对象、类都是基于C/C++当中结构体实现的
 1. 一个NSObject对象占用多少内存?
  答:可以看到NSObject_IMPL内部的isa是指向struct objc_class,说明isa是一个指针,在64位系统中
  该对象占用8个字节,但是实际分配了16个字节。我们需要用到下面两个函数

创建一个实例对象,至少需要多少内存?

#import <objc/runtime.h>
class_getInstanceSize([NSObject class]);

创建一个实例对象,实际上分配了多少内存?

#import <malloc/malloc.h>
malloc_size((__bridge const void *)obj);

打印如下:

NSObject *obj = [[NSObject alloc] init];
NSLog(@"%zd",class_getInstanceSize([NSObject class]));
NSLog(@"%zd",malloc_size((__bridge void *const)obj));

OCDemo[25726:949760] 8
OCDemo[25726:949760] 16

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

Objective-C的runtime是开源的,苹果开源官网下载:objc4
实例对象:一个类的具体实例。
实例对象的结构体数据结构如下:
struct objc_object {
private:
    isa_t isa;
public:
    // ISA() assumes this is NOT a tagged pointer object
    Class ISA();

    // getIsa() allows this to be a tagged pointer object
    Class getIsa();
    ...(省略)
}
NSObject *obj1 = [[NSObject alloc] init];
NSObject *obj2 = [[NSObject alloc] init];

object1、object2是NSObject的instance对象(实例对象)它们是不同的两个对象,分别占据着两块不同的内存

类:类是定义同一类所有对象的变量和方法的蓝图或原型(维基百科)

类的结构体数据结构如下:

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() { 
        return bits.data();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData);
    }
    ...(省略)
}
struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;

    method_array_t methods; // 类对象方法信息
    property_array_t properties;    // 属性信息
    protocol_array_t protocols;     // 协议信息
    ...(省略)
struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;  // 对象占用的内存大小
#ifdef __LP64__
    uint32_t reserved;
#endif

    const uint8_t * ivarLayout;
   
    const char * name;  // 类名
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;  // 成员变量列表

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;

    method_list_t *baseMethods() const {
        return baseMethodList;
    }
};
图片.png

元类:元类其实就是描述类对象的类

NSObject *obj3 =[[NSObject alloc]init];
Class objMetaClass1 = objc_getMetaClass("NSObject");
Class objMetaClass2 = object_getClass([NSObject class]);
Class objMetaClass3 = object_getClass(object_getClass(obj3));
NSLog(@"%p %p %p",objMetaClass1,objMetaClass2,objMetaClass3);

OCDemo[2418:112622] 0x7fff97fdb0f0 0x7fff97fdb0f0 0x7fff97fdb0f0
objMetaClass1、objMetaClass2、objMetaClass3就是NSObject的meta-class对象(元类对象)
图片.png
BOOL isMetaClass = class_isMetaClass([NSObject class]); // 是否是元类

方法的调用流程

通过以上信息我们就了解到了类、对象、元类之间的关系,那么类方法和对象方法的调用过程是怎样的呢??如下图所示:
图片.png 图片.png 图片.png

isa、superclass总结

图片.png
图片.png
开头的面试题应该心中都有答案了吧~
上一篇下一篇

猜你喜欢

热点阅读