美文网首页
iOS底层 - 对象的本质&isa分析

iOS底层 - 对象的本质&isa分析

作者: degulade | 来源:发表于2021-07-16 16:14 被阅读0次

前言

OC是一个面向对象的编程语言,对象就是我们整个编写代码的过程中,最为频繁接触到的一个东西,那么什么是对象呢?在上一篇文章iOS底层 - 结构体内存对齐中,我们了解到:对象的本质就是结构体。那么这个结论怎么验证呢?那么下面开始一探究竟。

一.了解clang

在探究对象本质之前先介绍一下clang:

  • Clang是⼀个C语⾔、C++、Objective-C语⾔的轻量级编译器
  • Clang将⽀持其普通lambda表达式、返回类型的简化处理以及更好的处理constexpr关键字
  • Clang是⼀个由Apple主导编写,基于LLVM的C/C++/Objective-C编译器
  • 2013年4⽉,Clang已经全⾯⽀持C++11标准,并开始实现C++1y特性(也就是C++14,这是C++的下⼀个⼩更新版本)
  • Clang是⼀个C++编写、基于LLVM、发布于LLVM BSD许可证下的C/C++/Objective-C/Objective-C++编译器。

二.编译oc文件为c++文件

  1. 直接命令行编译 :
//  把⽬标⽂件编译成c+
clang -rewrite-objc main.m -o main.cpp 
  1. xcode安装的时候顺带安装了xcrun命令,xcrun命令在clang的基础上进⾏了⼀些封装,要更好⽤⼀些:
//  模拟器
xcrun -sdk iphonesimulator clang -arch arm64 -rewrite- objc main.m -o main-arm64.cpp 
//  真机
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main arm64.cpp 
  1. 成功生成如下文件:


三.分析c++文件

开发main.cpp文件,搜索LGPerson

LGPerson
上图所示,我们可以看到对象在底层的本质是一个结构体。

跟踪NSObject_IMPL结构类型可以看到如图下:


isa objc

上图所示,可以得出objc底层调用就是objc_object

  • id class = [class new] 为什么我们id 类型可以获取所有的属性类型而且不需要加,因为他的底层就是id
@property (nonatomic, strong) NSString *KCName;
  • 为什么属性自带set 和get 方法 根据底层跟踪如下:
// @implementation LGPerson
// 方法 getter
static NSString * _I_LGPerson_kcName(LGPerson * self, SEL _cmd) {
    return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_kcName))
}

static void _I_LGPerson_setKcName_(LGPerson * self, SEL _cmd, NSString *kcName) { (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_kcName)) = kcName; }
// @end
  • 总结:对象本质就是一个结构体,属性成员变量实现了 get 和 set 方法.
  • 流程分析 :LGPerson —> 找到LGPerson_IMPL —> NSObject_IMPL -—> Class

四.isa分析

struct NSObject_IMPL {
    Class isa;
};

在上面的代码里,我们可以看到NSObject里面只有一个成员变量,那就是Class类型的isa。那么这isa是什么呢,我们就这个问题,继续探索下去,首先我们先看看他的类型Class在底层中的定义。

typedef struct objc_class *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;

可以看到,Class实际上就是一个objc_class *类型的结构体指针。

接下来我们来看看isa,在alloc流程的最后一步,就是通过initIsa方法将我们申请的内存地址和我们的Class绑定起来。

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;
    }
    isa = newisa;
}

在这些代码中间,有个非常重要的东西,就是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);
};

五.结构体和联合体

在上面的代码中,我们可以看到一个之前没有接触过的结构union,我们称之为联合体,那么他到底是什么,有什么特性呢,我们用下面这个例子来说明,首先看一段代码

// 结构体 : 共存
struct XHTeacher1 {
    char        *name;
    int         age;
    double      height ;
};

// 联合体 : 互斥
union XHTeacher2 {
    char        *name;
    int         age;
    double      height ;
};
  • 结构体(sturct)中的所有变量是“共存”的
    优点:海纳百川,有容乃大。只要你来,我都给你存下来
    缺点:内存空间的分配是粗放的,不管你用不用全都给你分配好位置

  • 联合体(union)中每个变量之间是“互斥”的
    优点:就是不够“包容”
    缺点:使用内存更为精细灵活,也节省了内存空间

相关文章

网友评论

      本文标题:iOS底层 - 对象的本质&isa分析

      本文链接:https://www.haomeiwen.com/subject/hvbjpltx.html