美文网首页
探索OC对象内存本质

探索OC对象内存本质

作者: 我是C | 来源:发表于2018-08-03 17:38 被阅读88次

看了一些资料,对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 */

相关文章

  • 探索OC对象内存本质

    看了一些资料,对oc更加深入了解,记录一下。一、得到对象占用内存直接上代码: 此时可能会有疑问,都是获取对象大小的...

  • OC对象的本质(上)

    iOS | OC对象本质 | Objective-C 什么是OC语言,OC对象、类的本质是什么,OC对象的内存布局...

  • 01.OC实例对象的本质

    你知道NSObject实例对象占用了多少内存么?要回答这道题,我们要探索下OC实例对象的本质,而实例对象的本质其实...

  • iOS底层isa结构分析

    在介绍正文之前,首先需要理解一个概念:OC对象的本质是什么? OC对象本质 在探索oc对象本质前,先了解一个编译器...

  • OC对象原理探究(下)

    介绍正文前,我们思考一个问题,什么是对象?或者说OC对象的本质是什么? 对象本质以及拓展 在探索oc对象本质前,先...

  • 探寻OC对象的本质

    iOS底层原理总结 - 探寻OC对象的本质 面试题:一个NSObject对象占用多少内存? 探寻OC对象的本质,我...

  • iOS-底层原理7:isa与类关联的原理

    问题 OC对象的本质到底是什么?里面到底是什么结构呢? 探索 在探索oc对象本质前,先了解一个编译器:clang ...

  • OC关于在MRC模式下的内存管理学习

    内存管理 管理范围 任何继承NSObject的对象 只有OC对象才需要进行内存管理的本质原理 1.OC对象在堆中 ...

  • 内存管理

    1.只有OC对象才需要进行内存管理的本质原因 --1.OC对象存放于堆中 --2.非OC对象存在栈中(栈内存会被系...

  • Ios面试复习--MRC内存管理

    1.只有OC对象才需要进行内存管理的本质原因 --1.OC对象存放于堆中 --2.非OC对象存在栈中(栈内存会被系...

网友评论

      本文标题:探索OC对象内存本质

      本文链接:https://www.haomeiwen.com/subject/zehuvftx.html