Class解读

群组
Flutter,iOS,Android,Python等,闲聊工作&技术&问题;
个人主页:https://waitwalker.cn
telegram群链接:https://t.me/joinchat/Ej0o0A1ntlq5ZFIMzzO5Pw

类的声明

首先我们在runtime.h文件中看到objc_class的结构声明:


/// 类的声明结构

struct objc_class {

Class _Nonnull isa OBJC_ISA_AVAILABILITY;//isa 指针

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

/* Use `Class` instead of `struct objc_class *` */

以上是runtime暴露给我们的有关Class的接口,而在objc-runtime-new.h文件中有objc_class结构的完整声明:


// MARK: - class的完整声明结构

struct objc_class : objc_object {

// Class ISA;

Class superclass;//指向父类指针

cache_t cache; // formerly cache pointer and vtable //缓存一些指针和虚表

class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags // 含有class_rw_t,内部存储方法,属性,遵循的协议等

// class_rw_t指针

class_rw_t *data() {

return bits.data();

}

// set bits

void setData(class_rw_t *newData) {

bits.setData(newData);

}

...下面未列出

}

可以看出,objc_class中有几个比较重要的成员变量和函数:

superclass

指向父类的指针

cache_t

cache里面存的是指针和虚表,为了方法调用时,快速查询,提高效率.其结构声明如下:


// MARK: - cache_t结构声明

struct cache_t {

struct bucket_t *_buckets;

mask_t _mask;

mask_t _occupied;

...下面未列出

}

bucket_t

// MARK: - bucket_t声明结构

struct bucket_t {

private:

// IMP-first is better for arm64e ptrauth and no worse for arm64.

// SEL-first is better for armv7* and i386 and x86_64.

#if __arm64__

MethodCacheIMP _imp;

cache_key_t _key;

#else

cache_key_t _key;

MethodCacheIMP _imp;

#endif

public:

inline cache_key_t key() const { return _key; }

inline IMP imp() const { return (IMP)_imp; }

inline void setKey(cache_key_t newKey) { _key = newKey; }

inline void setImp(IMP newImp) { _imp = newImp; }

void set(cache_key_t newKey, IMP newImp);

};

这个bucket_t类似于一个hash表,其没一个cache_key_t对应一个imp.其关系如下图:

bucket_t

class_data_bits_t结构

// MARK: - class_data_bits_t声明结构

struct class_data_bits_t {

class_rw_t* data() {

return (class_rw_t *)(bits & FAST_DATA_MASK);

}

void setData(class_rw_t *newData)

{

assert(!data() || (newData->flags & (RW_REALIZING | RW_FUTURE)));

// Set during realization or construction only. No locking needed.

// Use a store-release fence because there may be concurrent

// readers of data and data's contents.

uintptr_t newBits = (bits & ~FAST_DATA_MASK) | (uintptr_t)newData;

atomic_thread_fence(memory_order_release);

bits = newBits;

}

...其它未列出

}

class_data_bits_t中成员函数 data()函数,返回一个class_rw_t结构指针;

class_rw_t

// MARK: - class_rw_t结构声明

struct class_rw_t {

// Be warned that Symbolication knows the layout of this structure.

uint32_t flags;

uint32_t version;

const class_ro_t *ro;

method_array_t methods;

property_array_t properties;

protocol_array_t protocols;

Class firstSubclass;

Class nextSiblingClass;

char *demangledName;

#if SUPPORT_INDEXED_ISA

uint32_t index;

#endif

void setFlags(uint32_t set)

{

OSAtomicOr32Barrier(set, &flags);

}

void clearFlags(uint32_t clear)

{

OSAtomicXor32Barrier(clear, &flags);

}

// set and clear must not overlap

void changeFlags(uint32_t set, uint32_t clear)

{

assert((set & clear) == 0);

uint32_t oldf, newf;

do {

oldf = flags;

newf = (oldf | set) & ~clear;

} while (!OSAtomicCompareAndSwap32Barrier(oldf, newf, (volatile int32_t *)&flags));

}

};

class_rw_t中的成员变量class_ro_t:

class_ro_t

// MARK: - class_ro_t结构声明

struct class_ro_t {

uint32_t flags;

uint32_t instanceStart;

uint32_t instanceSize;

#ifdef __LP64__

uint32_t reserved;

#endif

const uint8_t * ivarLayout;

const char * name;

method_list_t * baseMethodList;

protocol_list_t * baseProtocols;

const ivar_list_t * ivars;

const uint8_t * weakIvarLayout;

property_list_t *baseProperties;

method_list_t *baseMethods() const {

return baseMethodList;

}

};

        class_ro_t有method_list_t,protocol_list_t,ivar_list_t,property_list_t等成员变量这些成员变量的作用是存储编译后已确定的一些信息.

        class_rw_t中同样有method_array_t(方法列表),property_array_t(属性列表),protocol_array_t(协议列表),这三者均继承自list_array_tt,list_array_tt可以扩充,内部分别存储的是method_list_t,property_list_t和protocol_list_t;这个三个成员变量主要是方便在运行时为类提供拓展能力.

编译后class内存布局


// Person

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Person : NSObject

- (void)name;

@end

NS_ASSUME_NONNULL_END

int main(int argc, const char * argv[]) {

@autoreleasepool {

Class newClass = objc_allocateClassPair(objc_getClass("NSObject"), "newClass", 0);

objc_registerClassPair(newClass);

id newObject = [[newClass alloc]init];

NSLog(@"%s",class_getName([newObject class]));

NSLog(@"Hello, World!");

Person *person = [Person new];

[person name];

[person name];

Class cls = [Person class];

}

return 0;

}

我们来看cls这个class在编译后的内存布局,首先我们在main入口函数里面继续初始化一个Class cls = [Person class];然后运行一下获取当前cls的内存地址,然后将断点放过.

Person class address

然后在_objc_init()函数(runtime初始化之前)加一个断点:

_objc_init()

用lldb命令调试一下:

cls memory layout

        通过上图我们可以看到,name(“Person”)和baseMethodList这两个是有值,其它都为0x0000000000000000空指针,这个也反映出我们在Person中的最初定义:只有一个- (void)name方法,没有协议,成员变量,属性等.

我们从baseMethodList方法列表中第一个method_t

name func

这个method_t正好对应我们定义的name方法;而baseMethodList里面也就有1个方法:

baseMethodList count

类的实现realizeClass

realizeClass是类初始化函数,类的运行时初始化都要调用此函数.


// MARK: - 类的初始化

/***********************************************************************

* realizeClass

* Performs first-time initialization on class cls,

* including allocating its read-write data.

* Returns the real class structure for the class.

* Locking: runtimeLock must be write-locked by the caller

**********************************************************************/

static Class realizeClass(Class cls)

{

runtimeLock.assertLocked();

const class_ro_t *ro;

class_rw_t *rw;

Class supercls;

Class metacls;

bool isMeta;

if (!cls) return nil;

if (cls->isRealized()) return cls;

assert(cls == remapClass(cls));

// fixme verify class is not in an un-dlopened part of the shared cache?

ro = (const class_ro_t *)cls->data();

if (ro->flags & RO_FUTURE) {

// This was a future class. rw data is already allocated.

rw = cls->data();

ro = cls->data()->ro;

cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);

} else {

// Normal class. Allocate writeable class data.

rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);

rw->ro = ro;

rw->flags = RW_REALIZED|RW_REALIZING;

cls->setData(rw);

}

isMeta = ro->flags & RO_META;

rw->version = isMeta ? 7 : 0; // old runtime went up to 6

// Choose an index for this class.

// Sets cls->instancesRequireRawIsa if indexes no more indexes are available

cls->chooseClassArrayIndex();

if (PrintConnecting) {

_objc_inform("CLASS: realizing class '%s'%s %p %p #%u",

cls->nameForLogging(), isMeta ? " (meta)" : "",

(void*)cls, ro, cls->classArrayIndex());

}

// Realize superclass and metaclass, if they aren't already.

// This needs to be done after RW_REALIZED is set above, for root classes.

// This needs to be done after class index is chosen, for root metaclasses.

supercls = realizeClass(remapClass(cls->superclass));

metacls = realizeClass(remapClass(cls->ISA()));

#if SUPPORT_NONPOINTER_ISA

// Disable non-pointer isa for some classes and/or platforms.

// Set instancesRequireRawIsa.

bool instancesRequireRawIsa = cls->instancesRequireRawIsa();

bool rawIsaIsInherited = false;

static bool hackedDispatch = false;

if (DisableNonpointerIsa) {

// Non-pointer isa disabled by environment or app SDK version

instancesRequireRawIsa = true;

}

else if (!hackedDispatch && !(ro->flags & RO_META) &&

0 == strcmp(ro->name, "OS_object"))

{

// hack for libdispatch et al - isa also acts as vtable pointer

hackedDispatch = true;

instancesRequireRawIsa = true;

}

else if (supercls && supercls->superclass &&

supercls->instancesRequireRawIsa())

{

// This is also propagated by addSubclass()

// but nonpointer isa setup needs it earlier.

// Special case: instancesRequireRawIsa does not propagate

// from root class to root metaclass

instancesRequireRawIsa = true;

rawIsaIsInherited = true;

}

if (instancesRequireRawIsa) {

cls->setInstancesRequireRawIsa(rawIsaIsInherited);

}

// SUPPORT_NONPOINTER_ISA

#endif

// Update superclass and metaclass in case of remapping

cls->superclass = supercls;

cls->initClassIsa(metacls);

// Reconcile instance variable offsets / layout.

// This may reallocate class_ro_t, updating our ro variable.

if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro);

// Set fastInstanceSize if it wasn't set already.

cls->setInstanceSize(ro->instanceSize);

// Copy some flags from ro to rw

if (ro->flags & RO_HAS_CXX_STRUCTORS) {

cls->setHasCxxDtor();

if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {

cls->setHasCxxCtor();

}

}

// Connect this class to its superclass's subclass lists

if (supercls) {

addSubclass(supercls, cls);

} else {

addRootClass(cls);

}

// Attach categories

methodizeClass(cls);

return cls;

}

        下面我们来验证class_ro_t确定时机.还是之前的步骤,先编译一次获取到cls的内存地址.在static Class realizeClass(Class cls){}入口添加一个条件断点,运行:

condition breakpoint

然后lldb调试cls:

访问 class_data_bits_t 指针的内容:(class_data_bits_t) $2755 = (bits = 4294971696)

获取 class_rw_t:


(class_rw_t) $2757 = {

flags = 128

version = 8

ro = 0x0000000000000008

methods = {

list_array_tt<method_t, method_list_t> = {

= {

list = 0x0000000000000000

arrayAndFlag = 0

}

}

}

properties = {

list_array_tt<property_t, property_list_t> = {

= {

list = 0x0000000100000f84

arrayAndFlag = 4294971268

}

}

}

protocols = {

list_array_tt<unsigned long, protocol_list_t> = {

= {

list = 0x0000000100001110

arrayAndFlag = 4294971664

}

}

}

firstSubclass = nil

nextSiblingClass = nil

demangledName = 0x0000000000000000 <no value available>

}

class_rw_t

        我们可以看到class_rw_t中的成员变量ro = 0x0000000000000008在运行前已经有值了.获取一下ro:


(lldb) p $2757.ro

(const class_ro_t *) $2758 = 0x0000000000000008

由于lldb输出class_ro_t报错:


(lldb) p *$2758

error: Couldn't apply expression side effects : Couldn't dematerialize a result variable: couldn't read its memory

我们直接单步断点,往下走两步,直接获取ro的值:

class_ro_t

        我们发现const指针 class_ro_t已经在编译期把类的相关信息(方法,属性,成员变量,协议)确定了.而类的初始化方法执行之后,编译期的ro赋值给了运行期的rw的成员变量ro了.


ro = (const class_ro_t *)cls->data();

if (ro->flags & RO_FUTURE) {

// This was a future class. rw data is already allocated.

rw = cls->data();

ro = cls->data()->ro;

cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);

} else {

// 将ro等信息赋值给class_rw_t

// Normal class. Allocate writeable class data.

rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);

rw->ro = ro;

rw->flags = RW_REALIZED|RW_REALIZING;

cls->setData(rw);

}

我们知道class_rw_t中除了class_ro_t成员变量外,还有以下三个成员变量:


method_array_t methods;

property_array_t properties;

protocol_array_t protocols;

        这三个成员变量的赋值操作在static void methodizeClass(Class cls)()函数中,分别遍历ro中的方法,协议,属性列表分别加入到class_rw_t的methods,protocols,properties中.


// MARK: - 将ro中的方法,属性,协议等列表添加到rw中对应的三个成员变量列表中

/***********************************************************************

* methodizeClass

* Fixes up cls's method list, protocol list, and property list.

* Attaches any outstanding categories.

* Locking: runtimeLock must be held by the caller

**********************************************************************/

static void methodizeClass(Class cls)

{

runtimeLock.assertLocked();

bool isMeta = cls->isMetaClass();

auto rw = cls->data();

auto ro = rw->ro;

// Methodizing for the first time

if (PrintConnecting) {

_objc_inform("CLASS: methodizing class '%s' %s",

cls->nameForLogging(), isMeta ? "(meta)" : "");

}

// 遍历ro中方法将其加入到rw methods中

// Install methods and properties that the class implements itself.

method_list_t *list = ro->baseMethods();

if (list) {

prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));

rw->methods.attachLists(&list, 1);

}

// 遍历ro中属性列表将其加入到 rw properties中

property_list_t *proplist = ro->baseProperties;

if (proplist) {

rw->properties.attachLists(&proplist, 1);

}

// 遍历ro中协议列表将其加入到 rw protocols中

protocol_list_t *protolist = ro->baseProtocols;

if (protolist) {

rw->protocols.attachLists(&protolist, 1);

}

// Root classes get bonus method implementations if they don't have

// them already. These apply before category replacements.

if (cls->isRootMetaclass()) {

// root metaclass

addMethod(cls, SEL_initialize, (IMP)&objc_noop_imp, "", NO);

}

// Attach categories.

category_list *cats = unattachedCategoriesForClass(cls, true /*realizing*/);

attachCategories(cls, cats, false /*don't flush caches*/);

if (PrintConnecting) {

if (cats) {

for (uint32_t i = 0; i < cats->count; i++) {

_objc_inform("CLASS: attached category %c%s(%s)",

isMeta ? '+' : '-',

cls->nameForLogging(), cats->list[i].cat->name);

}

}

}

if (cats) free(cats);

#if DEBUG

// Debug: sanity-check all SELs; log method list contents

for (const auto& meth : rw->methods) {

if (PrintConnecting) {

_objc_inform("METHOD %c[%s %s]", isMeta ? '+' : '-',

cls->nameForLogging(), sel_getName(meth.name));

}

assert(sel_registerName(sel_getName(meth.name)) == meth.name);

}

#endif

}

总结

        编译期:通过objc_class->data()获取class_ro_t,class的method,property,protocol,ivar等已确定被赋值给class_ro_t,class_to_t只读,说明编译期类的内存布局已固定.
        运行期:通过realizeClass()类初始化函数,给class_rw_t分配空间,将class_ro_t赋值给class_rw_t的成员变量ro.将ro中的方法,协议,属性列表分别加入到class_rw_t的methods,protocols,properties中.class_rw_t提供了运行期这些成员变量的扩展性.

编译后源码库

编译后的源码放在Github, 如果对你有帮助,请给一个star吧!

博客地址&相关文章

博客地址: https://waitwalker.github.io/

系列文章:

1. Runtime源码编译

2. objc_object解读

3. Method解读

4. Class解读

5. Ivar objc_property_t Protocol解读

6. Block解读

7. Retain&Release解读

8. Autorelease解读

9. Weak解读

10. msgSend()解读


  转载请注明: waitwalker Class解读

  目录