阅读 507

NSObject的本质

**NSObject**的底层实现

@interface NSObject {
    Class isa
}
@end

struct NSObject_IMPL {
    Class isa;
};

typedef struct objc_class *Class;
复制代码

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

NSObject *object = [[NSObject alloc] init];
// NSObject对象本质是一个结构体,结构体只有一个isa成员变量,它是一个指针。
// 这个指针在64位系统上占用8个字节

#import <objc/runtime.h>
#import <malloc/malloc.h>

// NSObject实例对象的内存大小  8
NSLog(@"%zd", class_getInstanceSize([NSObject class]));

// obj指针所指向的内存大小  16  (实际分配大小)
NSLog(@"%zd", malloc_size((__bridge const void *)(obj)));
复制代码

OC对象的分类

  1. 实例对象(instance
  2. 类对象(class
  3. 元类对象(meta-class

实例对象

  • 实例对象中存储:成员变量(isa指针)

类对象每个类只有一个类对象

类对象中存储信息:
isa指针
superClass指针
③ 类的属性(@property)、类的对象方法(instance method
④ 类的协议(protocol)、类的成员变量(ivar
⑤ 类的其他描述信息

NSObject *obj = [[NSObject alloc] init];
Class objClass1 = [obj class];
Class objClass2 = object_getClass(obj);
Class objClass3 = [NSObject class];
Class objClass4 = [objClass1 class];

objClass1、objClass2和objClass3是同一个类对象

类对象调用class方法,返回的是它本身
复制代码

元类对象每个类只有一个元类对象

元类对象和类对象的内存结构是一样的,都是Classobject_class结构体),主要包括:
isa指针
superClass指针
③ 类方法
④ 其他信息

NSObject *obj = [[NSObject alloc] init];
//将类对象传入,得到元类对象
Class metaClass1 = object_getClass([NSObject class]);
Class metaClass2 = object_getClass([obj class]);
Class metaClass3 = object_getClass(object_getClass(obj));

//判断是否是元类对象
class_isMetaClass(metaClass1);
复制代码

总结:

  • object_getClass方法返回的是对象的isa指针所指向的对象

  • class方法返回的是对象本身

  • 64bit开始,实例对象的isa需要进行按位与运算(&ISA_MASK)才能得到类对象的地址

  • 实例对象的superClass指针直接指向类对象地址

为什么isa指针需要进行按位与才能获取类对象地址呢?

union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }
    Class cls;
    uintptr_t bits;
# if __arm64__#   define ISA_MASK        0x0000000ffffffff8ULL
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
    struct {
    // 0代表普通的指针,存储着Class,Meta-Class对象的内存地址。
    // 1代表优化后的使用位域存储更多的信息。
    uintptr_t nonpointer        : 1;
    uintptr_t has_assoc         : 1; // 是否有设置过关联对象,如果没有,释放时会更快
    uintptr_t has_cxx_dtor      : 1; // 是否有C++析构函数,如果没有,释放时会更快
    // 存储着Class、Meta-Class对象的内存地址信息
    uintptr_t shiftcls          : 33; // MACH_VM_MAX_ADDRESS 0x1000000000
    uintptr_t magic             : 6; // 用于在调试时分辨对象是否未完成初始化
    uintptr_t weakly_referenced : 1; // 是否有被弱引用指向过。
    uintptr_t deallocating      : 1; // 对象是否正在释放

    // 引用计数器是否过大无法存储在isa中
    // 如果为1,那么引用计数会存储在一个叫SideTable的类的属性中
    uintptr_t has_sidetable_rc  : 1;
    uintptr_t extra_rc          : 19; // 里面存储的值是引用计数器减1#
    define RC_ONE   (1ULL<<45)#
    define RC_HALF  (1ULL<<18)    };
#endif

};
复制代码

isa是一个指针,占8byte,总共64bitOC采用联合体定义构建isa,并使用位域存储着不同的配置信息,其中类对象地址只占用33位,isa指针通过跟掩码进行按位与计算后,就能拿到对应的类对象地址。

ISA_MASK转为二进制之后为:111111111111111111111111111111111000
复制代码

知识点:

① 数据类型占内存的位数

数据类型                            32位    64位
char                                1       1

short int                           2       2

int                                 4       4

long int                            4       8

long long int                       8       8

char*                               4       8

float                               4       4

double                              8       8
复制代码

1个字节Byte)等于8bit)二进制位

② iOS平台默认内存对齐系数为8

iOS堆内存分配时,都是以16的倍数进行分配

内存对齐理解

③ 将OC代码转为C++代码

clang -rewrite-objc main.m -o main.cpp
复制代码

将OC代码转为指定cpu架构的C++代码

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
复制代码

iOS源码下载地址

搜索objc4下载运行时源码

文章分类
iOS
文章标签