对象的本质

对象的本质是什么?
对象在底层的本质是结构体!
谢谢大家观看!!

开个玩笑=-= 真是个幽默的帅小伙


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的析构器,如果有析构函数,则需要做析构逻辑,如果没有,则可以更快的释放对象。
shiftclsx86架构中占用3 - 46位,表示存储类指针的值。开启指针优化的情况下,在arm64架构中占用3 - 35位,只占用了33位。
magicx86架构中占用47 - 52位,在arm64架构中占用36 - 41位,用于调式器判断当前对象是真的对象还是没有初始化的空间。
weakly_referencedx86架构中占用53位,在arm64架构中占用42位,标志对象是否被指向或者曾经指向一个ARC的弱变量,没有弱引用的对象可以更快释放。
unusedx86架构中占用54位,在arm64架构中占用43位,标志对象是否正在释放内存。
has_sidetable_rcx86架构中占用55位,在arm64架构中占用44位,表示当对象引用计数大于10时,则需要借用该变量存储进位。
extra_rcx86架构中占用56 - 63位,在arm64架构中占用45 - 63位,当表示该对象的引用计数值时,实际上是引用计数值减1,例如:如果对象的引用计数为10,那么extra_rc为9,如果引用计数大于10,则需要使用到has_sidetable_rc

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值