美文网首页ios
isa与cls的关联探索

isa与cls的关联探索

作者: 张墩墩 | 来源:发表于2020-09-13 16:22 被阅读0次

联合体

联合体(共用体):一种特殊的数据类型,允许在相同的内存位置存储不同的数据类型。

  • 所有成员占用同一段内存,修改一个成员会影响其余所有成员。
    共用体使用了内存覆盖技术,同一时刻只能保存一个成员的值,如果对新的成员赋值,就会把原来成员的值覆盖掉
  • 各变量是互斥的——缺点就是不够包容; 但优点是内存使用更为精细灵活,也节省了内存空间。
  • 共用体占用的内存等于最长的成员占用的内存

Clang

  • Clang是一个由Apple主导编写,基于LLVMC/C++/Objective-C编译器
  • 作用:借助Clang可以将oc文件输出成C++文件,方便探究其底层的一些结构、逻辑、底层的实现原理等

简单的使用命令

//把目标文件编译成c++文件
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

`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 (手机)

接下来借助Clang来简单的探索一下OC的对象。

oc对象探索

  • 准备oc对象LGPerson,并添加属性name。此时LGPeson应该有nameisa两个成员。
#import <Foundation/Foundation.h>
@interface LGPerson : NSObject

@property (copy, nonatomic) NSString *name;

@end

@implementation LGPerson
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
    }
    return 0;
}

  • 转化输出C++文件,找到LGPerson对象
#ifndef _REWRITER_typedef_LGPerson
#define _REWRITER_typedef_LGPerson
typedef struct objc_object LGPerson;
typedef struct {} _objc_exc_LGPerson;
#endif

extern "C" unsigned long OBJC_IVAR_$_LGPerson$_name;
struct LGPerson_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    NSString *_name;
};
// @property (copy, nonatomic) NSString *name;

/* @end */
// @implementation LGPerson

static NSString * _I_LGPerson_name(LGPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_name)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);

static void _I_LGPerson_setName_(LGPerson * self, SEL _cmd, NSString *name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct LGPerson, _name), (id)name, 0, 1); }
// @end
  • 从输出的C++文件可以看到LGPersonobjc_object结构体

可以看到定义的属性_name,但是没有发现isa。反而有一个struct NSObject_IMPL NSObject_IVARS;
c++文件中搜索NSObject_IMPL {会发现缺少的isa在里面。但是却变成了Class类型。

struct NSObject_IMPL {
    Class isa;
};

我们在探索alloc &init时,有initInstanceIsa是将isacls相关联。那么应该是在关联这里做什么。下面进入源码探索一下关联的操作。

isa关联cls

  • isacls的源码关联操作
if (!zone && fast) {
        obj->initInstanceIsa(cls, hasCxxDtor);
 } else {
        // Use raw pointer isa on the assumption that they might be
        // doing something weird with the zone or RR.
        obj->initIsa(cls);
 }
关联操作

可以看到isacls的关联,其实就是将cls向右移动了3位,赋值保存在isashiftcls的区域中。因此LGPerson其实就保存在isa

那么通过object_getClass获取Class时,源码底层肯定是从isa中通过某种操作取出我们需要的Class
进入object_getClass源码

object_getClass

可以发现object_getClass方法就是获取isa

isa

最终发现是isabits成员或者&ISA_MASK的结果在转成Class返回

isa是如何存储cls

  • isa类型是isa_t是一个联合体(union)
union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }
    //提供了cls 和 bits ,两者是互斥关系
    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};

从isa_t的定义有Class cls和一个uintptr_t bits互斥的成员及结构体中宏定义的位域ISA_BITFIELD

ISA_BITFIELD
  • nonpointer:表示是否对isa指针开启指针优化
    0:纯isa指针
    1:不止是类对象地址,isa中包含了类信息、对象的引用计数
  • has_assoc:关联对象标志位,0没有,1存在
  • has_cxx_dtor:该对象是否有C++或者Objc析构器,如果有析构函数,则需要做析构逻辑, 如果没有,则可以更快的释放对象
  • shiftcls:存储类指针的值。开启指针优化的情况下,在arm64架构中有33位用来存储类指针。
  • magic:用于调试器判断当前对象是真的对象还是没有初始化的空间
  • weakly_referenced:对象是否被指向或者曾经指向一个ARC的弱变量,没有弱引用的对象可以更快释放
  • deallocating:标志对象是否正在释放内存
  • has_sidetable_rc:当对象引用技术大于10 时,则需要借用该变量存储进位
  • extra_rc:当表示该对象的引用计数值,实际上是引用计数值减 1.
    例如,如果对象的引用计数为 10,那么extra_rc为 9。如果引用计数大于 10, 则需要使用到下面的has_sidetable_rc

arm64ISA_BITFIELD位域的存储的信息

arm64下ISA_BITFIELD位域的存储

位域中shiftcls位置的33存储类的指针的值。那么取出isa存储的位域中的shiftcls的值,应该是我们的存储的Class

x86_64下存储cls的流程

  • 断点clsLGPerson时进入isa源码
    isa源码
    断点newisa的初始化isa_t newisa(0);赋初值的操作newisa.bits = ISA_MAGIC_VALUE;分别输出:
    isa初始化、赋初值

从结果可以看到cls和bits赋值后,位域中存储的信息,也发生了变化。nonpointer代表对isa开启指针优化。magic值成了59。因为这里只是进行了赋初值那么这59肯定来自初始值ISA_MAGIC_VALUE

x86_64结构图中看magic是从47位开始的占6位。将初始值ISA_MAGIC_VALUE输出二进制进行查看。看到从magic位置的二进制111011是十进制的59

FB82AADE-0403-4F57-9575-7D5E53022FEF.png
  • cls的赋值newisa.shiftcls = (uintptr_t)cls >> 3;
    cls转化成uintptr_t右移3位存储在shiftcls区域。
    存储cls

了解了cls的存储的位置及操作。那么通过存储的逆向操作应该能从isa中取出LGPerson

  • isa中获取LGPerson
isa获取cls
移动操作

通过反向对isa的反向操作确实获得了LGPerson
但是通过object_getClass获取Class时是isa.bits& ISA_MASK操作。

return (Class)(isa.bits & ISA_MASK);

ISA_MASK = 0x00007ffffffffff8ULL其实就是上面的移动抹0操作的位与运算。遮住不需要的信息,保留需要的信息。

位与运算

通过isa的与cls的关联存储及object_getClass获取源码也就可以理解c++文件中isaClass类型了。

 struct NSObject_IMPL {
    Class isa;
};

相关文章

  • isa与cls的关联探索

    联合体 联合体(共用体):一种特殊的数据类型,允许在相同的内存位置存储不同的数据类型。 所有成员占用同一段内存,修...

  • iOS Objective-C isa 走位分析

    iOS Objective-C isa 走位分析 1. 判断isa是对象的第一个属性 isa<-->cls的关联 ...

  • OC 类探索(一)

    一、isa->类和元类 上篇文章分析了对象的isa底层实现以及是如何与cls关联的,这边文章继续分析类的结构。 对...

  • iOS-底层原理 :isa与类关联的原理

    今天探索的主要目的是理解类与isa是如何关联的 探索之前,先了解一个编译器:clang Clang clang是一...

  • OC_底层_isa探究

    isa与类关联的原理 OC对象的本质 先简单简介llbd相关知识: 在代码中探索对象本质: 在 main.cpp ...

  • isa详解-位域

    ``` union isa_t { Class cls; uintptr_t bits; Str...

  • iOS 底层探索:isa与类关联的原理

    iOS 底层探索: 学习大纲 OC篇[/p/9d73ee7aae64] 前言 这篇主要内容探索 类与isa是如何关...

  • iOS底层原理探索—isa与类的关联

    今天来分析下alloc中的3个核心中的initInstanceIsa方法(将内存地址与cls绑定),首先我们先了解...

  • 四、isa 指针关联类

    主要内容:1.OC对象的本质2.isa 与 类的关联原理3.isa 与 类的关联验证 1.OC对象的本质 先了解编...

  • 系统底层源码分析(14)——isa

    联合体 前面提到isa关联了类,那么isa是什么?isa的本质是联合体: 关联 它是怎么关联类的,比如Person...

网友评论

    本文标题:isa与cls的关联探索

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