OC类原理-对象的本质

在说到对象的本质前,我们按照惯例通过一段代码来拉开我们对象的序幕。

image.png

同样是4个BOOL类型的结构体,凭什么struct1要占3个字节,而struct2只需要1个字节了,这是神马操作?

小伙伴,这就是位域!

位域

在内存存储过程中,有些信息(男/女)存储实际只需要1位就能解决,但使用过程,但却使用了1整个字节(1字节 = 8位),这种就是内存的浪费了。而为了避免这种内存浪费,就有了位域。

位域表示二进制位从右往左排布。比如上面struct1struct2的各个变量分别的内存排布如下:

image.png

联合体

与结构体对应的数据类型就是联合体了,也叫共同体,我们下面通过一段示例了解下什么叫联合体。

image.png

从代码结果可以得到结果:

  • 联合体中修改其中的某个变量会覆盖其他变量的值。
  • 联合体所有的变量共用一块内存,变量之间互斥。所以联合体也叫共同体

联合体的优点就是内存更为节省,缺点是不够包容!!

对象的本质

研究本质前,我们了解下编译器Clang。

  • Clang 是一个C语言、C++、Objective-C语言的轻量级编译器,是由Apple主导编写的
  • Clang 主要用于把源文件编译成底层文件,比如把main.m 文件编译成main.cpp、main.o或者可执行文件。便于观察底层的逻辑结构,便于我们探究底层。

Clang 终端编译命令

//UIKit报错
clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk main.m
// xcrun命令基于clang基础上进行了封装更好用
//3、模拟器编译
 xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp 
//4、真机编译
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main- arm64.cpp
复制代码

将下面代码通过Clang编译:

{
    NSString * height;
}
@property(nonatomic,  copy)NSString   *LWname;
@property(nonatomic,assign)NSInteger  age;
@end

@implementation LWPerson

@end

int main(int argc, char * argv[]) {
  
    @autoreleasepool {
    }
    return 0;
}
复制代码

编译结果:

#define _REWRITER_typedef_NSObject
typedef struct objc_object NSObject;
typedef struct {} _objc_exc_NSObject;
#endif

struct NSObject_IMPL {
	Class isa;
};

extern "C" unsigned long OBJC_IVAR_$_LWPerson$_LWname;
extern "C" unsigned long OBJC_IVAR_$_LWPerson$_age;
struct LWPerson_IMPL {
	struct NSObject_IMPL NSObject_IVARS;
	double  height;
	NSString *_LWname;
	NSInteger _age;
};

// @property(nonatomic, copy)NSString *LWname;
// @property(nonatomic,assign)NSInteger age;
/* @end */

// @implementation LWPerson

复制代码

通过编译结果我们分析如下:

  • Class类型实际是 objc_class类型的结构体指针,objc_class是所有类的底层实现。在此我们猜测isa可能跟类信息存在着重要的关联。
  • NSObject的底层实际是objc_object,在OC中基本上所有的对象都是继承NSObject,但是真正的底层实现是objc_object的结构体类型。而obcj_object的成员变量结构体都是Class isa

所以,我们的对象实际情况:

  • 对象的本质是结构体
  • Person的isa是继承NSObject中的isa
  • NSObject中只有一个成员变量那就是isa

isa关联类

通过alloc流程 alloc –> _objc_rootAlloc –> callAlloc –> _objc_rootAllocWithZone –> _class_createInstanceFromZone,断点在 obj->initInstanceIsa,进入obj->initInstanceIsa
inline void 断点进入initIsa

我们发现了isa的结构类型是isa_t,进入isa_t

image.png

从这段源码我们可以知道:

isa的结构体是isa_t,查看isa_t结构体,可以看到内部实际内容存储格式是宏定义ISA_BITFIELD,继续查看:

image.png

这里我们就知道了,isa除了包含指针,还以位域的形式存储了很多其他信息,具体信息跟位域就不一一说明,可以直接查看。

  • nonpointer :是否对isa指针进行优化 0表示纯isa,1表示包含了其他信息
  • has_accos :是否关联对象,0表示未关联,1表示有关联
  • hac_cxx_dtor :是否有析构函数,如果有析构函数,则需要做析构逻辑,没有,则释放对象
  • shiftcls :类,指针地址,开启指针优化的情况下,在arm64架构中有33位用来存储类指针,x86_64架构中占44
  • magic :调试器判断当前对象是真的对象还是没有初始化的空间
  • weakly_referenced :对象是否被指向或者曾经指向一个ARC的弱变量,没有弱引用的对象可以更快释放
  • unused :是否被释放
  • has_sidetable_rc :是否有其他列表存储,当对象引用计数大于10时,则需要借用该变量存储进位
  • extra_rc :该对象的引用计数值,实际上引用计数值减1,例如,如果对象的引用计数为10,那么extra_rc9,如果大于10,就需要用到上面的has_sidetable_rc

isa结构总结

  • isa分为nonpointer类型和非nonpointer。非nonpointer类型只是一个纯指针,nonpointer还包含了类的信息
  • isa是联合体+位域的方式存储信息的。采用这种方式的有点就是节省大量内存。万物皆对象,只要是对象就有isa指针,大量的isa就占用了很多内存,联合体公用一块内存节省了部分内存,而位域更是在节省内存的基础上存储了信息,可以说isa指针的内存得到了充分的利用。
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享