类的声明
首先我们在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_tclass_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.cn/
系列文章:
网友评论