对象的本质是什么?
对象在底层的本质是结构体!
谢谢大家观看!!
开个玩笑=-= 真是个幽默的帅小伙
1.对象的本质
让我们一步步来探究一下对象的本质是啥
我们先在main.m文件里面创建一个类
@interface LGPerson : NSObject
@property (nonatomic, strong) NSString *Zstr;
@property (nonatomic, strong) NSMutableArray *ZArray;
@end
@implementation LGPerson
//
@end
随后我们将这个类 clang编译一下
clang -rewrite-objc main.m -o main.cpp
编译很顺利 一次过 如果遇到UIKit
报错问题的 可以执行以下命令
clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-13.0.0 -isysroot /
Applications/Xcode.app/Contents/Developer/Platforms/
iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.0.sdk main.m
然后我们就得到了一个cpp文件
这个cpp文件是又长又粗 我们直接搜索一下我们创建的LGPerson
吧
然后我们就找到了这个
struct LGPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *_Zstr;
NSMutableArray *_ZArray;
};
原来我们的LGPerson
在底层是一个struct
这是不是就说明了 对象在底层的本质 其实就是一个结构体
当这个时候 我们对内在的属性 进行变换的时候 我们会发现不管内部属性如何变换 有一个值是一直存在的
struct NSObject_IMPL NSObject_IVARS;
我们在全局搜索一下 找到了这个结构体
struct NSObject_IMPL {
Class isa;
};
看来所有的都离不开isa啊等会再探究一下isa
先让我们来了解一下
结构体、联合体、位域
什么是结构体 我们已经了然于心
那什么是联合体呢
// 联合体 : 互斥
union LGTeacher2 {
char *name;
int age;
double height ;
};
我们经过打印可以发现 在联合体中 只会有一个元素存在值 其他的元素都是脏数据
那位域又是啥呢
struct LGCar1 {
BOOL front;
BOOL back;
BOOL left;
BOOL right;
};
我们创建了一个这样的结构体,而这个结构体占用了四个字节 其实一个BOOL
用一个bit就足够了 而我们这样足足浪费了三个字节
struct LGCar2 {
BOOL front : 1;
BOOL back : 1;
BOOL left : 1;
BOOL right : 1;
};
然后我们再次打印一下LGCar
我们可以发现 这个时候的LGCar
只占用了一个字节
这时候就会发现占用的内存 大大的缩小了
让我们来总结一下
结构体(struct):所有变量是“共存”的——优点是“有容乃⼤”且全⾯;缺点也很明显struct内存空间的分配是粗放的,不管⽤不⽤,全分配。
联合体(union)中是各变量是“互斥”的——缺点就是不够“包容”;但优点是内存使⽤更为精细灵活,也节省了内存空间
位域:就是每一个二进制位 就是一个位域,在C语言中允许在一个结构体中以位为单位来指定其成员所占内存长度
这个时候 我又有一个想法 如果 我定义了一个1位域的int属性 赋值的时候会发生什么呢
他还是只会占一位 多出的字节会截取调 而且位域不会报错 只会给予警告 优先级还是很高 很智能的
了解了结构体、联合体、位域之后 我们在看一看isa
在流程_class_createInstanceFromZone
--> initInstanceIsa
--> initIsa
, 我们发现了isa的初始化代码.
inline void
objc_object::initIsa(Class cls, bool nonpointer, UNUSED_WITHOUT_INDEXED_ISA_AND_DTOR_BIT bool hasCxxDtor)
{
ASSERT(!isTaggedPointer());
isa_t newisa(0);
if (!nonpointer) {
newisa.setClass(cls, this);
} else {
ASSERT(!DisableNonpointerIsa);
ASSERT(!cls->instancesRequireRawIsa());
#if SUPPORT_INDEXED_ISA
ASSERT(cls->classArrayIndex() > 0);
newisa.bits = ISA_INDEX_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
newisa.bits = ISA_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
# if ISA_HAS_CXX_DTOR_BIT
newisa.has_cxx_dtor = hasCxxDtor;
# endif
// 对象绑定上类
newisa.setClass(cls, this);
#endif
newisa.extra_rc = 1;
}
// This write must be performed in a single store in some cases
// (for example when realizing a class because other threads
// may simultaneously try to use the class).
// fixme use atomics here to guarantee single-store and to
// guarantee memory order w.r.t. the class index table
// ...but not too atomic because we don't want to hurt instantiation
isa = newisa;
}
我们看看 isa_t
isa_t是啥,进去看看
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
uintptr_t bits;
private:
// Accessing the class requires custom ptrauth operations, so
// force clients to go through setClass/getClass by making this
// private.
Class cls;
public:
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD; // defined in isa.h
};
bool isDeallocating() {
return extra_rc == 0 && has_sidetable_rc == 0;
}
void setDeallocating() {
extra_rc = 0;
has_sidetable_rc = 0;
}
#endif
void setClass(Class cls, objc_object *obj);
Class getClass(bool authenticated);
Class getDecodedClass(bool authenticated);
};
isa_t 是一个联合体,提供了两个成员,cls和bits,由联合体的定义所知,这两个成员是互斥的,也就意味着,当初始化isa指针时,有两种初始化方式: 通过cls初始化, 或者通过bits初始化.
在这个联合体下面又有一个关键点 ISA_BITFIELD
在这里 我们就可以跟深层次的看到isa做了一些什么 内部又有什么呢
# if __arm64__
// ARM64 simulators have a larger address space, so use the ARM64e
// scheme even when simulators build for ARM64-not-e.
# if __has_feature(ptrauth_calls) || TARGET_OS_SIMULATOR
# ...
# else
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_HAS_CXX_DTOR_BIT 1
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t unused : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 19
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
# endif
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
# define ISA_HAS_CXX_DTOR_BIT 1
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t unused : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)
# else
# error unknown architecture for packed isa
# endif
在x86
架构下
在arm64
架构下
nonpointer
在0位,占用1位,表示是否对isa指针开启指针优化。0:纯isa指针,1:不止是类对象地址,isa包含了类信息、对象的引用计数等。
has_assoc
在1位,占用1位,表示关联对象标志位,0:没有,1:有。
has_cxx_dtor
在2位,占用1位,表示该对象是否有C++或者Objc的析构器,如果有析构函数,则需要做析构逻辑,如果没有,则可以更快的释放对象。
shiftcls
在x86
架构中占用3 - 46位,表示存储类指针的值。开启指针优化的情况下,在arm64
架构中占用3 - 35位,只占用了33位。
magic
在x86
架构中占用47 - 52位,在arm64
架构中占用36 - 41位,用于调式器判断当前对象是真的对象还是没有初始化的空间。
weakly_referenced
在x86
架构中占用53位,在arm64
架构中占用42位,标志对象是否被指向或者曾经指向一个ARC的弱变量,没有弱引用的对象可以更快释放。
unused
在x86
架构中占用54位,在arm64
架构中占用43位,标志对象是否正在释放内存。
has_sidetable_rc
在x86
架构中占用55位,在arm64
架构中占用44位,表示当对象引用计数大于10时,则需要借用该变量存储进位。
extra_rc
在x86
架构中占用56 - 63位,在arm64
架构中占用45 - 63位,当表示该对象的引用计数值时,实际上是引用计数值减1,例如:如果对象的引用计数为10,那么extra_rc
为9,如果引用计数大于10,则需要使用到has_sidetable_rc
。