ios

isa与cls的关联探索

2020-09-13  本文已影响0人  windy_3c22

联合体

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

Clang

简单的使用命令

//把目标文件编译成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对象探索

#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;
}

#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

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

struct NSObject_IMPL {
    Class isa;
};

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

isa关联cls

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

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

arm64ISA_BITFIELD位域的存储的信息

arm64下ISA_BITFIELD位域的存储

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

x86_64下存储cls的流程

从结果可以看到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的存储的位置及操作。那么通过存储的逆向操作应该能从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;
};
上一篇 下一篇

猜你喜欢

热点阅读