OC对象的本质

一、本质

  1. OC对象的本质是C和C++中的结构体

  2. OC代码转C++代码:clang -rewrite-objc main.m -o main.cpp

    • 转化成什么平台的代码,不同平台支持的代码不一样。例如:Windows、MacOS,iOS。
    • iOS:模拟器(i386/x86_64)、32bit(armv7)、64bit(arm64)
    • 命令:xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main_arm64.cpp
  3. Class是一个指向结构体的指针。
    typedef struct objc_class *Class 指针在64位机器中占8个字节
    sizeof():是针对类型的,得到一个类型占多少内存。sizeof不是函数,是运算符,编译过后就变成了常数,不存在函数调用(跟宏定义有点类似)。如:sizeof(int)、sizeof(double)、sizeof(struct Student_IMPL)

NSObject *obj = [[NSObject alloc] init];
// 获取NSObject类的实例对象成员变量所占用的大小 >> 8,返回结构体内存对齐过后的成员变量大小
NSLog(@"%zd", class_getInstanceSize([NSObject class]));
// 获取obj所指向内存的大小 >> 16
NSLog(@"%zd", malloc_size((__bridge const void *)obj));
sizeof(struct Student_IMPL);   // 结构体Student_IMPL的大小
sizeof(obj) --> 8   // obj是个指针,占8个字节
sizeof(int) --> 4    // int占4个字节
int a = 10;
sizeof(a) --> 4
// May be unaligned depending on class's ivars.
uint32_t unalignedInstanceSize() {
    assert(isRealized());
    return data()->ro->instanceSize;
}

// Class's ivar size rounded up to a pointer-size boundary.
uint32_t alignedInstanceSize() {
    // 返回内存对齐过后的大小(align对齐)
    return word_align(unalignedInstanceSize());
}

// class_getInstanceSize的实现
size_t class_getInstanceSize(Class cls)
{
    if (!cls) return 0;
    return cls->alignedInstanceSize();
}

A、题:一个NSObject对象占用多少内存?

答:1、系统分配了16个字节给NSObject对象(通过malloc_size函数获得) 2、 但NSObject对象内部只使用了8个字节的空间(64位环境下,通过class_getInstanceSize函数获得)


注意:Class isa只占8个字节,但是[NSObject alloc]会分配16个字节。OC对象分配的内存最少16个字节

// calloc函数分配内存大小的时候调用的size函数,所有OC对象最少会分配16个字节
size_t instanceSize(size_t extraBytes) {
    size_t size = alignedInstanceSize() + extraBytes;
    // CF requires all objects be at least 16 bytes.
    if (size < 16) size = 16;
    return size;
}
struct Student_IMPL {
//    struct NSObject_IMPL NSObject_IVARS;
    Class isa;   // 8字节
    int _no;  // 4字节
    int _age;  // 4字节
};

s->_no = 4; 通过指针直接访问成员变量
Student:NSObject 占16个字节,8 + 4 + 4
结构体内存对齐:结构体的最终大小必须是最大成员大小的倍数。

struct NSObject_IMPL {
    Class isa;
};

struct Person_IMPL {
//    struct NSObject_IMPL NSObject_IVARS;
    Class isa;    // 8个字节
    int _age;     // 4个字节
};  // 16个字节  内存对齐:结构体的最终大小必须是最大成员大小的倍数


struct Student_IMPL {
//    struct Person_IMPL Person_IVARS;
    Class isa;   // 16个字节
    int _no;     // 4个字节
};   // 16个字节  Class isa 里面实际用到的只有12个字节,下面的4个字节直接使用空地址

二、属性

对于成员变量的属性,只会在该类的结构体中申明对应的成员变量,不会有对应的get,set方法。

方法是不会放在实例对象里面的,因为实例对象可以创建很多份,一一不同,但是方法只需要保存一份即可。方法保存在类对象的方法列表里面

实例方法保存在类对象的方法列表里面,类方法保存在元类对象的方法列表里面(一个类的类对象和元类对象有且只有一份)。


B、题:创建一个实例对象,至少需要多少内存?
答:
1、#import <objc/runtime.h>  
2、class_getInstanceSize([NSObject class]);
3、sizeof(struct NSObject_IMPL);
故:至少需要8个字节

C、题:创建一个实例对象,实际分配多少内存?
答:
1、#import <malloc/malloc.h>  
2、malloc_size((__bridge const void *)obj);
故:实际分配了16个字节,分配的字节数是16的倍数

// calloc分配内存的时候遵守的内存对齐
#define NANO_MAX_SIZE  256  /** Buckets sized {16, 32, 48, 64, 80, 96, 112, ...}*/

我们可以总结内存对齐为两个原则:
原则 1. 前面的地址必须是后面的地址正数倍,不是就补齐。
原则 2. 整个Struct的地址必须是最大字节的整数倍。

底层实现:
// NSObject:
struct NSObject_IMPL {
    Class isa;
};

// Class
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 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();
    ...
}

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;

    Class firstSubclass;
    Class nextSiblingClass;
    ...
}

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;
    }
};
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354

推荐阅读更多精彩内容