Objective-C的instance对象本质

一、Objective-C的本质

我们平时编写的Objective-C代码,底层是通过C\C++代码实现的。
编译过程.png

所以Objective-C的面向对象都是基于C\C++的数据结构

  • 1、将Objective-C代码转换为C\C++代码
在终端输入下面的命令把OC代码的.m文件转换成.cpp文件
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc [OC文件] -o [.cpp文件]

eg:把ViewController.m文件转出ViewController.cpp文件
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc ViewController.m -o ViewController.cpp
  • 1.1、Objective-C转成C++代码后是一个结构体,并且其中有一个Class类型的属性isa
    Objective-C的instance对象本质_第1张图片
    NSObject本质是结构体.png
  • 1.2、Class是一个struct objc_class类型的指针
    Objective-C的instance对象本质_第2张图片
    Class的类型.png
  • 1.3、struct objc_class类型结构
    Objective-C的instance对象本质_第3张图片
    struct objc_class类型结构.png
  • 1.4、NSObject类转换为C\C++代码源码
// 1、NSObject
struct NSObject_IMPL {
    Class isa;
};

// 2、Class
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;

// 3、struct objc_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 *` */

  • 2、Objective-C的对象、类主要是基于C\C++的结构体实现的
  • 3、NSObject底层原理
    Objective-C的instance对象本质_第4张图片
    NSObject底层原理.png

二、NSObject对象在内存中的布局和所占大小

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //1、NSObject实例对象占用内容大小
    NSObject *revan_Obj = [[NSObject alloc] init];
    
    /* Returns the size of instances of a class.
       表示[NSObject class]实例的内存大小
     */
    NSLog(@"class_getInstanceSize([NSObject class]):%zd\n", class_getInstanceSize([NSObject class]));
    
    /* Returns size of given ptr
     
       表示分配给revan_Obj这个对象的内存大小
     */
    NSLog(@"malloc_size((__bridge const void *)(revan_Obj)):%zd", malloc_size((__bridge const void *)(revan_Obj)));
}

//输出
class_getInstanceSize([NSObject class]):8
malloc_size((__bridge const void *)(revan_Obj)):16

1、class_getInstanceSize([NSObject class])的大小为什么是8?

  • 从上面对NSObject底层原理的分析可知,NSObject中只有一个isa指针,我们知道在ARM64位机中会给指针分配8个内存单元

2、malloc_size((__bridge const void *)(revan_Obj)):的大小为什么是16?

  • OC源码
  • [NSObject alloc]的时候为实例对象分配内存,我们可以通过查看源码发现分配的内存大小不小于16(在查看源码是直接搜索allocWithZone)
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;
}

三、自定义类

1、自定义一个RevanPerson类

#import 

@interface RevanPerson : NSObject {
    int _no;
    int _age;
}

@end

#import "RevanPerson.h"

@implementation RevanPerson

@end
  • 1.1、RevanPerson有2个成员变量时(测试代码)
- (void)viewDidLoad {
    [super viewDidLoad];
    
    //1、RevanPerson实例对象占用内容大小
    RevanPerson *revan_person = [[RevanPerson alloc] init];
    
    /* Returns the size of instances of a class.
       表示[RevanPerson class]实例的内存大小
     */
    NSLog(@"class_getInstanceSize([RevanPerson class]):%zd\n", class_getInstanceSize([RevanPerson class]));
    
    /* Returns size of given ptr
     
       表示分配给revan_person这个对象的内存大小
     */
    NSLog(@"malloc_size((__bridge const void *)(revan_person)):%zd", malloc_size((__bridge const void *)(revan_person)));
}

//打印输出
class_getInstanceSize([RevanPerson class]):16
malloc_size((__bridge const void *)(revan_person)):16
  • 1.2、RevanPerson有3个成员变量
#import 

@interface RevanPerson : NSObject {
    int _no;
    int _age;
    int _height;
}

@end

  • 测试代码
- (void)viewDidLoad {
    [super viewDidLoad];
    
    //1、RevanPerson实例对象占用内容大小
    RevanPerson *revan_person = [[RevanPerson alloc] init];
    
    
    /* Returns the size of instances of a class.
       表示[RevanPerson class]实例的内存大小
     */
    NSLog(@"class_getInstanceSize([RevanPerson class]):%zd\n", class_getInstanceSize([RevanPerson class]));
    
    /* Returns size of given ptr
     
       表示分配给revan_person这个对象的内存大小
     */
    NSLog(@"malloc_size((__bridge const void *)(revan_person)):%zd", malloc_size((__bridge const void *)(revan_person)));
}

//打印输出
class_getInstanceSize([RevanPerson class]):24
malloc_size((__bridge const void *)(revan_person)):32
  • 同样是自定义RevanPerson类,成员变量不同,造成输出也不同
  • 分析:RevanPerson有2个成员变量时的内存分配情况
分析:RevanPerson有2个成员变量时的内存分配情况
1、底层实现如下
struct RevanPerson_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    int _no;
    int _age;
};
2、发现其中包含一个struct NSObject_IMPL类型的成员变量NSObject_IVARS
3、struct NSObject_IMPL结构如下,这个就是上面研究的NSObject的底层实现,
因为struct NSObject_IMPL结构体内部只有一个Class 类型的成员变量isa,
我们知道Class其实是(struct objc_class *),是一个指针,在ARM64架构下会给指针分配8个内存空间
struct NSObject_IMPL {
    Class isa;
};
4、在ARM64架构下会给int类型分配4个内存单元
struct RevanPerson_IMPL {
    struct NSObject_IMPL NSObject_IVARS; //8
    int _no; //4
    int _age; //4
};
所以,在RevanPerson为2个成员变量的情况下,会被分配16个内存空间

分析:malloc_size((__bridge const void *)(revan_person)),
revan_person对象占用16个内存空间
1、下面是在创建实例对象时为对象分配内存空间源码,可以看出会为一个对象至少分配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;
}
2、由于我们现在RevanPerson有2个成员变量,只占用16个内存空间,
小于等于 创建对象时的16个内存空间,所以系统不会再为revan_person对象分配更多内存空间
Objective-C的instance对象本质_第5张图片
Person2个成员变量.png
  • 分析:RevanPerson有3个成员变量时的内存分配情况
分析:RevanPerson有3个成员变量时的内存分配情况
1、底层实现如下
struct RevanPerson_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    int _no;
    int _age;
    int _height;
};
2、发现其中包含一个struct NSObject_IMPL类型的成员变量NSObject_IVARS
3、struct NSObject_IMPL结构如下,这个就是上面研究的NSObject的底层实现,
因为struct NSObject_IMPL结构体内部只有一个Class 类型的成员变量isa,
我们知道Class其实是(struct objc_class *),是一个指针,在ARM64架构下会给指针分配8个内存空间
struct NSObject_IMPL {
    Class isa;
};
4、在ARM64架构下会给int类型分配4个内存单元
struct RevanPerson_IMPL {
    struct NSObject_IMPL NSObject_IVARS; // 8
    int _no; // 4
    int _age; // 4
    int _height; // 4
};
RevanPerson_IMPL占用内存空间大小计算: 8 + 4 + 4 + 4 = 20,但是打印输出是24
这是因为结构体中有一个内存对齐的要求,在RevanPerson_IMPL这个结构体中各个变量占用最大内存是8,
所以这个RevanPerson_IMPL结构体以后分配的内存就会按照 8 的整数倍来分配,因为20不是8的整数倍,
所以RevanPerson_IMPL这个结构体会被分配24个内存地址

分析:malloc_size((__bridge const void *)(revan_person)),
revan_person对象占用32个内存空间
1、创建对象是为对象分配内存空间源码,可以看出会为一个对象至少分配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;
}
2、由于我们现在RevanPerson有3个成员变量,24个内存空间,但是在内存分配中也是有内存对齐的原则,由于为每一个对象分配的内存空间至少16个,所以以后类实例分配的内存空间都是16的倍数,所以是32
Objective-C的instance对象本质_第6张图片
Person3个成员变量.png

2、int 和 NSInteger 的区别

  • RevanPerson新增一个NSInteger类型的成员变量
#import 

@interface RevanPerson : NSObject {
    int _no;
    int _age;
    int _height;
//    int _cla;
    NSInteger _cla;
}

@end

  • 测试代码
- (void)viewDidLoad {
    [super viewDidLoad];
    
    //1、RevanPerson实例对象占用内容大小
    RevanPerson *revan_person = [[RevanPerson alloc] init];
    
    
    /* Returns the size of instances of a class.
       表示[RevanPerson class]实例的内存大小
     */
    NSLog(@"class_getInstanceSize([RevanPerson class]):%zd\n", class_getInstanceSize([RevanPerson class]));
    
    /* Returns size of given ptr
     
       表示分配给revan_person这个对象的内存大小
     */
    NSLog(@"malloc_size((__bridge const void *)(revan_person)):%zd", malloc_size((__bridge const void *)(revan_person)));
}

//打印输出
class_getInstanceSize([RevanPerson class]):32
malloc_size((__bridge const void *)(revan_person)):32

  • int 和 NSInteger 的区别分析
上面已经相信分析了结构体分配原则,这里就不在细说,现在这个结构体和上面结构体不同的是这个结构体多了一个NSInteger类型的成员变量,现在占用了32个内存空间
struct RevanPerson_IMPL {
    struct NSObject_IMPL NSObject_IVARS;// 8
    int _no;//4
    int _age;//4
    int _height;//4
    NSInteger _cla;//?
};
RevanPerson_IMPL内存空间:8 + 4 + 4 + 4 + ? = 32  = 20 + ?
由于结构体的内存对齐原则,也就是这个结构体的内存大小是8的整数倍
在上面的分析RevanPerson_IMPL结构体中分配了24个内存单元,但是实际使用了20个内存单元(8 + 4 + 4 + 4),
struct RevanPerson_IMPL {
    struct NSObject_IMPL NSObject_IVARS;// 8
    int _no;//4
    int _age;//4
    int _height;//4
};
所以可以推断NSInteger类型占用的内存空间大于4

你可能感兴趣的:(Objective-C的instance对象本质)