探索OC对象内存本质

看了一些资料,对oc更加深入了解,记录一下。
一、得到对象占用内存
直接上代码:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSObject *obj = [[NSObject alloc] init];
        NSLog(@"%zd",class_getInstanceSize([NSObject class]));
        NSLog(@"%zd",malloc_size((__bridge const void *)obj));
     }
    return 0;
}
2018-08-03 16:49:14.945686+0800 NSObject本质[6765:234469] 8
2018-08-03 16:49:14.945962+0800 NSObject本质[6765:234469] 16
Program ended with exit code: 0

此时可能会有疑问,都是获取对象大小的方法,为什么不一样呢。
一起来看看源码:地址
1.https://opensource.apple.com/tarballs/
2.找到objc4
3.找到最新版本(数字最大的那个)
4.搜索class_getInstanceSize
源码如下:

// Class's ivar size rounded up to a pointer-size boundary.
    uint32_t alignedInstanceSize() {
        return word_align(unalignedInstanceSize());
    }

看注释说,返回成员变量的大小
所以说malloc_size是实际占用的内存大小

二、为什么是16字节?
我们再来看源码,搜索+ (id)allocWithZone:(struct _NSZone *)zone

// Replaced by ObjectAlloc
+ (id)allocWithZone:(struct _NSZone *)zone {
    return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone);
}
//_objc_rootAllocWithZone
//class_createInstance
//_class_createInstanceFromZone
//instanceSize

最终找到:

    // Class's ivar size rounded up to a pointer-size boundary.
    uint32_t alignedInstanceSize() {
        return word_align(unalignedInstanceSize());
    }

    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;
    }

alignedInstanceSize 由一可知,这个方法返回8字节(class_getInstanceSize)小于16字节,也就是说,创建一个NSObject 对象,最小占用内存为16字节

三、用C++重写OC 代码,看看NSObject对象本质
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
找到NSObject 实现

struct NSObject_IMPL {
    Class isa;
};

是一个结构体,结构体只有一个成员,就是isa
isa是一个typedef struct objc_class *Class;类型的指针,我们都知道指针占用8个字节。
我们看看NSObject 对象的内存编码:

image.png

前8个字节是isa指针地址,后面的字节都是00。所以后面8个字节是没有用到的

三、用C++重写OC 代码,看看自定义Student对象本质

@interface Student:NSObject
{
    @public
    int _no;
    int _age;
}
@end

@implementation Student
@end

重写之后找到代码如下:

struct NSObject_IMPL {//8个字节 【NSObject alloc】 16个字节
    Class isa;
};

struct Student_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    int _no;
    int _age;
};

对于继承于NSObject的对象, 会包含一个父类NSObject的isa指针和两个成员变量。我们知道由于struct NSObject_IMPL NSObject_IVARS 占用8个字节。
(wait...不是16字节吗?[NSObject alloc] 16(8+4+4)个字节isa是8个字节,别搞混,搞混了回去再看一遍)
所以对于一个Student对象,他占用的字节数是:16字节,我们来验证一下

Student *stu = [[Student alloc] init];
stu->_age = 4;
stu->_no = 5;
NSLog(@"%zd",malloc_size((__bridge const void *)stu));
2018-08-03 17:26:52.725832+0800 NSObject本质[7110:255889] 16
image.png
image.png

前8字节是isa 指针的地址,后面的05 00 00 00 和 04 00 00 00,由于是小端读取,所以是高地址读取 00 00 00 05 和 00 00 00 04

通过db 命令,我们可以改内存里的值,如图:

image.png

x :读取内存
memory write 地址 数据 :更改成员变量的值

总结:

1.8字节 isa 指针 + 子类和父类(继承链上)的成员变量 == 对象内存大小。(内存对齐后)
2.instance对象内存最小为16字节。
3.instance对象内存大小是16字节的最小整数倍。
2.alloc 出来的instance对象 ,只有成员变量,没有方法。方法放在类对象中(因为只需要一份)。

**注释:
由于iOS系统会内存对齐,所以,创建出来的对象是16的整数倍
#define NANO_MAX_SIZE 256 /* Buckets sized {16, 32, 48, 64, 80, 96, 112, ...} */
calloc函数做了iOS系统级别的内存对齐

void *
malloc_zone_calloc(malloc_zone_t *zone, size_t num_items, size_t size)
{
    void *ptr;
    size_t alloc_size;
    if (malloc_check_start && (malloc_check_counter++ >= malloc_check_start)) {
        internal_check();
    }
    if (os_mul_overflow(num_items, size, &alloc_size) || alloc_size > MALLOC_ABSOLUTE_MAX_SIZE){
        errno = ENOMEM;
        return NULL;
    }

    ptr = zone->calloc(zone, num_items, size);
    
    if (malloc_logger) {
        malloc_logger(MALLOC_LOG_TYPE_ALLOCATE | MALLOC_LOG_TYPE_HAS_ZONE | MALLOC_LOG_TYPE_CLEARED, (uintptr_t)zone,
                (uintptr_t)(num_items * size), 0, (uintptr_t)ptr, 0);
    }
    return ptr;
}

补充:
对于属性:

@interface Person : NSObject
@property (nonatomic,assign) int  age;
@property (nonatomic,assign) int  age2;
@property (nonatomic,assign) int  age3;
@property (nonatomic,assign) int  age4;
@property (nonatomic,assign) int  age5;
@property (nonatomic,assign) int  age6;
@property (nonatomic,assign) int  age7;
@end
struct Person_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
};

// @property (nonatomic,assign) int age;
// @property (nonatomic,assign) int age2;
// @property (nonatomic,assign) int age3;
// @property (nonatomic,assign) int age4;
// @property (nonatomic,assign) int age5;
// @property (nonatomic,assign) int age6;
// @property (nonatomic,assign) int age7;
/* @end */

出现这种情况,其实c++ 重写 只是参考,本质还是

struct Person_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
        int  _age;
            .  
            .
            .
};

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