我们app的运行都会通过DYLD
加载动静态库,然后进入到_objc_init
函数,首先我们看一下这个函数中都干了什么?
1、_objc_init
函数
先上源码:
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
// fixme defer initialization until an objc-using image is found?
environ_init();
tls_init();
static_init();
lock_init();
exception_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
其中3、4、5行代码只是一些判断,我们不用管,下面执行了environ_init()
:
1.1environ_init()
void environ_init(void)
{
if (issetugid()) {
// All environment variables are silently ignored when setuid or setgid
// This includes OBJC_HELP and OBJC_PRINT_OPTIONS themselves.
return;
}
bool PrintHelp = false;
bool PrintOptions = false;
bool maybeMallocDebugging = false;
// Scan environ[] directly instead of calling getenv() a lot.
// This optimizes the case where none are set.
for (char **p = *_NSGetEnviron(); *p != nil; p++) {
if (0 == strncmp(*p, "Malloc", 6) || 0 == strncmp(*p, "DYLD", 4) ||
0 == strncmp(*p, "NSZombiesEnabled", 16))
{
maybeMallocDebugging = true;
}
if (0 != strncmp(*p, "OBJC_", 5)) continue;
if (0 == strncmp(*p, "OBJC_HELP=", 10)) {
PrintHelp = true;
continue;
}
if (0 == strncmp(*p, "OBJC_PRINT_OPTIONS=", 19)) {
PrintOptions = true;
continue;
}
const char *value = strchr(*p, '=');
if (!*value) continue;
value++;
for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
const option_t *opt = &Settings[i];
if ((size_t)(value - *p) == 1+opt->envlen &&
0 == strncmp(*p, opt->env, opt->envlen))
{
*opt->var = (0 == strcmp(value, "YES"));
break;
}
}
}
// Special case: enable some autorelease pool debugging
// when some malloc debugging is enabled
// and OBJC_DEBUG_POOL_ALLOCATION is not set to something other than NO.
if (maybeMallocDebugging) {
const char *insert = getenv("DYLD_INSERT_LIBRARIES");
const char *zombie = getenv("NSZombiesEnabled");
const char *pooldebug = getenv("OBJC_DEBUG_POOL_ALLOCATION");
if ((getenv("MallocStackLogging")
|| getenv("MallocStackLoggingNoCompact")
|| (zombie && (*zombie == 'Y' || *zombie == 'y'))
|| (insert && strstr(insert, "libgmalloc")))
&&
(!pooldebug || 0 == strcmp(pooldebug, "YES")))
{
DebugPoolAllocation = true;
}
}
// Print OBJC_HELP and OBJC_PRINT_OPTIONS output.
if (PrintHelp || PrintOptions) {
if (PrintHelp) {
_objc_inform("Objective-C runtime debugging. Set variable=YES to enable.");
_objc_inform("OBJC_HELP: describe available environment variables");
if (PrintOptions) {
_objc_inform("OBJC_HELP is set");
}
_objc_inform("OBJC_PRINT_OPTIONS: list which options are set");
}
if (PrintOptions) {
_objc_inform("OBJC_PRINT_OPTIONS is set");
}
for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
const option_t *opt = &Settings[i];
if (PrintHelp) _objc_inform("%s: %s", opt->env, opt->help);
if (PrintOptions && *opt->var) _objc_inform("%s is set", opt->env);
}
}
}
通过源码我们可以看出这个函数主要是根据一些判断条件打印一些环境变量和帮助文档,有人会说这些环境变量有什么用呢?我们可以在lldb
上用命令export OBJC_HELP=1
来打印环境变量:
objc[6393]: OBJC_PRINT_IMAGES: log image and library names as they are loaded
objc[6393]: OBJC_PRINT_IMAGE_TIMES: measure duration of image loading steps
objc[6393]: OBJC_PRINT_LOAD_METHODS: log calls to class and category +load methods
objc[6393]: OBJC_PRINT_INITIALIZE_METHODS: log calls to class +initialize methods
objc[6393]: OBJC_PRINT_RESOLVED_METHODS: log methods created by +resolveClassMethod: and +resolveInstanceMethod:
objc[6393]: OBJC_PRINT_CLASS_SETUP: log progress of class and category setup
objc[6393]: OBJC_PRINT_PROTOCOL_SETUP: log progress of protocol setup
objc[6393]: OBJC_PRINT_IVAR_SETUP: log processing of non-fragile ivars
objc[6393]: OBJC_PRINT_VTABLE_SETUP: log processing of class vtables
objc[6393]: OBJC_PRINT_VTABLE_IMAGES: print vtable images showing overridden methods
objc[6393]: OBJC_PRINT_CACHE_SETUP: log processing of method caches
objc[6393]: OBJC_PRINT_FUTURE_CLASSES: log use of future classes for toll-free bridging
objc[6393]: OBJC_PRINT_PREOPTIMIZATION: log preoptimization courtesy of dyld shared cache
objc[6393]: OBJC_PRINT_CXX_CTORS: log calls to C++ ctors and dtors for instance variables
objc[6393]: OBJC_PRINT_EXCEPTIONS: log exception handling
objc[6393]: OBJC_PRINT_EXCEPTION_THROW: log backtrace of every objc_exception_throw()
objc[6393]: OBJC_PRINT_ALT_HANDLERS: log processing of exception alt handlers
objc[6393]: OBJC_PRINT_REPLACED_METHODS: log methods replaced by category implementations
objc[6393]: OBJC_PRINT_DEPRECATION_WARNINGS: warn about calls to deprecated runtime functions
objc[6393]: OBJC_PRINT_POOL_HIGHWATER: log high-water marks for autorelease pools
objc[6393]: OBJC_PRINT_CUSTOM_RR: log classes with un-optimized custom retain/release methods
objc[6393]: OBJC_PRINT_CUSTOM_AWZ: log classes with un-optimized custom allocWithZone methods
objc[6393]: OBJC_PRINT_RAW_ISA: log classes that require raw pointer isa fields
objc[6393]: OBJC_DEBUG_UNLOAD: warn about poorly-behaving bundles when unloaded
objc[6393]: OBJC_DEBUG_FRAGILE_SUPERCLASSES: warn about subclasses that may have been broken by subsequent changes to superclasses
objc[6393]: OBJC_DEBUG_NIL_SYNC: warn about @synchronized(nil), which does no synchronization
objc[6393]: OBJC_DEBUG_NONFRAGILE_IVARS: capriciously rearrange non-fragile ivars
objc[6393]: OBJC_DEBUG_ALT_HANDLERS: record more info about bad alt handler use
objc[6393]: OBJC_DEBUG_MISSING_POOLS: warn about autorelease with no pool in place, which may be a leak
objc[6393]: OBJC_DEBUG_POOL_ALLOCATION: halt when autorelease pools are popped out of order, and allow heap debuggers to track autorelease pools
objc[6393]: OBJC_DEBUG_DUPLICATE_CLASSES: halt when multiple classes with the same name are present
objc[6393]: OBJC_DEBUG_DONT_CRASH: halt the process by exiting instead of crashing
objc[6393]: OBJC_DISABLE_VTABLES: disable vtable dispatch
objc[6393]: OBJC_DISABLE_PREOPTIMIZATION: disable preoptimization courtesy of dyld shared cache
objc[6393]: OBJC_DISABLE_TAGGED_POINTERS: disable tagged pointer optimization of NSNumber et al.
objc[6393]: OBJC_DISABLE_TAG_OBFUSCATION: disable obfuscation of tagged pointers
objc[6393]: OBJC_DISABLE_NONPOINTER_ISA: disable non-pointer isa fields
objc[6393]: OBJC_DISABLE_INITIALIZE_FORK_SAFETY: disable safety checks for +initialize after fork
看到这是不是想说,这是什么鬼?其实我们在仔细看一下,这些都是我们项目的一些环境变量,我们可以在xcode
:Edit Scheme
->Arguments
->Environment Variables
进行设置运行时环境变量,这些环境变量的具体使用运行时环境变量( Environment Variables )
- 添加
OS_ACTIVITY_MODE
为disable
,进行屏蔽系统日志 - 添加
OBJC_DISABLE_NONPOINTER_ISA
为YES
,可以进行isa
优化,可以使isa
存储更多的内容. - 添加
OBJC_PRINT_LOAD_METHODS
为YES
,可以打印类和分类的load
方法,对我们优化启动有帮助。
1.2tls_init()
主要进行线程与key
的绑定。
void tls_init(void)
{
#if SUPPORT_DIRECT_THREAD_KEYS
_objc_pthread_key = TLS_DIRECT_KEY;
pthread_key_init_np(TLS_DIRECT_KEY, &_objc_pthread_destroyspecific);
#else
_objc_pthread_key = tls_create(&_objc_pthread_destroyspecific);
#endif
}
1.3static_init()
这是是C++
静态构造函数的调用会在这里进行,在dyld
加载静态构造函数之前,libc
调用_objc_init()
,所以我们自己来做。(注释可得结论)
/***********************************************************************
* static_init
* Run C++ static constructor functions.
* libc calls _objc_init() before dyld would call our static constructors,
* so we have to do it ourselves.
**********************************************************************/
static void static_init()
{
size_t count;
auto inits = getLibobjcInitializers(&_mh_dylib_header, &count);
for (size_t i = 0; i < count; i++) {
inits[i]();
}
}
1.4lock_init()
void lock_init(void)
{
}
这个函数是空的,这里可能就是可以采用C
和C++
那一套锁的机制。
1.5exception_init()
这里是处理异常的一个函数,我们看一下源码:
/***********************************************************************
* exception_init
* Initialize libobjc's exception handling system.
* Called by map_images().
**********************************************************************/
void exception_init(void)
{
old_terminate = std::set_terminate(&_objc_terminate);
}
exception_init
初始化libobjc
异常处理系统。
/***********************************************************************
* _objc_terminate
* Custom std::terminate handler.
*
* The uncaught exception callback is implemented as a std::terminate handler.
* 1. Check if there's an active exception
* 2. If so, check if it's an Objective-C exception
* 3. If so, call our registered callback with the object.
* 4. Finally, call the previous terminate handler.
**********************************************************************/
static void (*old_terminate)(void) = nil;
static void _objc_terminate(void)
{
if (PrintExceptions) {
_objc_inform("EXCEPTIONS: terminating");
}
if (! __cxa_current_exception_type()) {
// No current exception.
(*old_terminate)();
}
else {
// There is a current exception. Check if it's an objc exception.
@try {
__cxa_rethrow();
} @catch (id e) {
// It's an objc object. Call Foundation's handler, if any.
(*uncaught_handler)((id)e);
(*old_terminate)();
} @catch (...) {
// It's not an objc object. Continue to C++ terminate.
(*old_terminate)();
}
}
}
这里进行异常的输出,首先检查这是否是一个异常,如果是,检查是否是Objective-C
异常,如果是,叫我们的注册回调对象uncaught_handler
函数,最后终止处理程序。
1.6_dyld_objc_notify_register
//
// Note: only for use by objc runtime
// Register handlers to be called when objc images are mapped, unmapped, and initialized.
// Dyld will call back the "mapped" function with an array of images that contain an objc-image-info section.
// Those images that are dylibs will have the ref-counts automatically bumped, so objc will no longer need to
// call dlopen() on them to keep them from being unloaded. During the call to _dyld_objc_notify_register(),
// dyld will call the "mapped" function with already loaded objc images. During any later dlopen() call,
// dyld will also call the "mapped" function. Dyld will call the "init" function when dyld would be called
// initializers in that image. This is when objc calls any +load methods in that image.
//
void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped,
_dyld_objc_notify_init init,
_dyld_objc_notify_unmapped unmapped);
看注释:仅供objc
运行时使用, 在注册映射,取消映射和初始化 objc
图像时要调用的处理程序。dyld
将使用包含objc-image-info
的镜像文件的数组来调用mapped
函数。
_dyld_objc_notify_register(&map_images, load_images, unmap_image)
有3个参数
map_images
load_images
-
unmap_image
我们来探索一下
2、map_images
首先我们看一下map_images
里面有什么?
/***********************************************************************
* map_images
* Process the given images which are being mapped in by dyld.
* Calls ABI-agnostic code after taking ABI-specific locks.
*
* Locking: write-locks runtimeLock
**********************************************************************/
void
map_images(unsigned count, const char * const paths[],
const struct mach_header * const mhdrs[])
{
mutex_locker_t lock(runtimeLock);
return map_images_nolock(count, paths, mhdrs);
}
这里就是
- 处理由
dyld
映射的给定图像。 - 取得
ABI
特定的锁后,调用与ABI
无关的代码。
继续走就进入map_images_nolock
函数,但是这个函数代码很长,里面都是一些打印和操作hCount
,经过分析我们定位到:
if (hCount > 0) {
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
map_images
执行的就是加载镜像文件,所以最终来到_read_images
读取镜像文件,这就是map_images_nolock
的重点。
2.1_read_images
_read_images
你如果打开源码会看到,里面这个函数有400行代码,太多了,我们只挑重点的分析。看下面一段源码:
if (!doneOnce) {
doneOnce = YES;
#if SUPPORT_NONPOINTER_ISA
// Disable non-pointer isa under some conditions.
# if SUPPORT_INDEXED_ISA
// Disable nonpointer isa if any image contains old Swift code
for (EACH_HEADER) {...}
# endif
# if TARGET_OS_OSX
// Disable non-pointer isa if the app is too old
// (linked before OS X 10.11)
if (dyld_get_program_sdk_version() < DYLD_MACOSX_VERSION_10_11) {...}
// Disable non-pointer isa if the app has a __DATA,__objc_rawisa section
// New apps that load old extensions may need this.
for (EACH_HEADER) {...}
# endif
#endif
if (DisableTaggedPointers) {
disableTaggedPointers();
}
initializeTaggedPointerObfuscator();
if (PrintConnecting) {...}
// namedClasses
// Preoptimized classes don't go in this table.
// 4/3 is NXMapTable's load factor
int namedClassesSize =
(isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
gdb_objc_realized_classes =
NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
allocatedClasses = NXCreateHashTable(NXPtrPrototype, 0, nil);
ts.log("IMAGE TIMES: first time tasks");
}
我折叠的部分都是一些打印。看这一段源码首先判断了一个doneOnce
字面意思,我们可以理解这里只执行一次,为什么只执行一次?我们看代码会发现有两行这个代码:
gdb_objc_realized_classes =
NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
allocatedClasses = NXCreateHashTable(NXPtrPrototype, 0, nil);
创建2张表:gdb_objc_realized_classes
和allocatedClasses
,我们看一下这两张表分别是存什么的?
// This is a misnomer: gdb_objc_realized_classes is actually a list of
// named classes not in the dyld shared cache, whether realized or not.
NXMapTable *gdb_objc_realized_classes; // exported for debuggers in objc-gdb.h
static Class getClass_impl(const char *name)
{
runtimeLock.assertLocked();
// allocated in _read_images
assert(gdb_objc_realized_classes);
// Try runtime-allocated table
Class result = (Class)NXMapGet(gdb_objc_realized_classes, name);
if (result) return result;
// Try table from dyld shared cache
return getPreoptimizedClass(name);
}
- 通过注释我们可以知道,这张表是存储所有的类,包括实现的和未实现的。
/***********************************************************************
* allocatedClasses
* A table of all classes (and metaclasses) which have been allocated
* with objc_allocateClassPair.
**********************************************************************/
static NXHashTable *allocatedClasses = nil;
- 这个就是存储被分配过的类和元类的表,就是已经实现的类和元类的表。
我们可以这样说gdb_objc_realized_classes
是包含allocatedClasses
的,既然是包含,是不是只创建一张表就够了,为什么苹果会创建两张表?主要是为了断开,让我们进行精确查找,因为有时候会发现有一些特殊情况的类没有被初始化,在当前allocatedClasses
这张表中找不到,我们没必要存储,虽然在gdb_objc_realized_classes
这张表中有,但是加载到的这个类并不一定是已经初始化的,我们每次查找不需要都带着一个大表进行查找。
类的重映射
继续上面的我们往下继续看:
// Discover classes. Fix up unresolved future classes. Mark bundle classes.
for (EACH_HEADER) {
// 从编译后的类列表中取出所有类,获取到的是一个classref_t类型的指针
classref_t *classlist = _getObjc2ClassList(hi, &count);
if (! mustReadClasses(hi)) {
// Image is sufficiently optimized that we need not call readClass()
continue;
}
bool headerIsBundle = hi->isBundle();
bool headerIsPreoptimized = hi->isPreoptimized();
for (i = 0; i < count; i++) {
// 数组中会取出OS_dispatch_queue_concurrent、OS_xpc_object、NSRunloop等系统类,例如CF、Fundation、libdispatch中的类。以及自己创建的类
Class cls = (Class)classlist[i];
// 通过readClass函数获取处理后的新类,
Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
// 初始化所有懒加载的类需要的内存空间 - 现在数据没有加载到的 - 连类都没有初始化的
if (newCls != cls && newCls) {
// Class was moved but not deleted. Currently this occurs
// only when the new class resolved a future class.
// Non-lazily realize the class below.
// 将懒加载的类添加到数组中
resolvedFutureClasses = (Class *)
realloc(resolvedFutureClasses,
(resolvedFutureClassCount+1) * sizeof(Class));
resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
}
}
}
这一段就是从类列表中遍历所有的类,并添加到对应的gdb_objc_realized_classes
和allocatedClasses
表中。
那么,这个是在什么地方添加的呢?我们可以看到这个函数的调用readClass
:
Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
const char *mangledName = cls->mangledName();
Class replacing = nil;
if (Class newCls = popFutureNamedClass(mangledName)) {
// This name was previously allocated as a future class.
// Copy objc_class to future class's struct.
// Preserve future's rw data block.
if (newCls->isAnySwift()) {...}
class_rw_t *rw = newCls->data();
const class_ro_t *old_ro = rw->ro;
memcpy(newCls, cls, sizeof(objc_class));
rw->ro = (class_ro_t *)newCls->data();
newCls->setData(rw);
freeIfMutable((char *)old_ro->name);
free((void *)old_ro);
addRemappedClass(cls, newCls);
replacing = cls;
cls = newCls;
}
if (headerIsPreoptimized && !replacing) {
// class list built in shared cache
// fixme strict assert doesn't work because of duplicates
// assert(cls == getClass(name));
assert(getClassExceptSomeSwift(mangledName));
} else {
addNamedClass(cls, mangledName, replacing);
addClassTableEntry(cls);
}
// for future reference: shared cache never contains MH_BUNDLEs
if (headerIsBundle) {
cls->data()->flags |= RO_FROM_BUNDLE;
cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
}
return cls;
}
在上面的一段代码中我们看到对类cls
进行了rw
和ro
的处理,那么这些是不是真的在这个方法里面处理呢?
我们进行断点调试之后发现,代码都执行完了都没有进入到这里,所以说明我们创建的类和系统方法的类都没有走这个方法,所以类的rw
数据填充并非在此处理。
通过if (Class newCls = popFutureNamedClass(mangledName))
这个判断我们得知,这里只是对未来待处理的类进行操作。
在往下,我们看到了添加表的代码:
addNamedClass(cls, mangledName, replacing);
addClassTableEntry(cls);
我们查看addNamedClass
:
static void addNamedClass(Class cls, const char *name, Class replacing = nil)
{
runtimeLock.assertLocked();
Class old;
if ((old = getClassExceptSomeSwift(name)) && old != replacing) {
inform_duplicate(name, old, cls);
// getMaybeUnrealizedNonMetaClass uses name lookups.
// Classes not found by name lookup must be in the
// secondary meta->nonmeta table.
addNonMetaClass(cls);
} else {
NXMapInsert(gdb_objc_realized_classes, name, cls);
}
assert(!(cls->data()->flags & RO_META));
// wrong: constructed classes are already realized when they get here
// assert(!cls->isRealized());
}
- 通过
NXMapInsert(gdb_objc_realized_classes, name, cls);
将类添加到我们上述所说的总表中。
继续查看addClassTableEntry
代码:
/***********************************************************************
* addClassTableEntry
* Add a class to the table of all classes. If addMeta is true,
* automatically adds the metaclass of the class as well.
* Locking: runtimeLock must be held by the caller.
**********************************************************************/
static void addClassTableEntry(Class cls, bool addMeta = true) {
runtimeLock.assertLocked();
// This class is allowed to be a known class via the shared cache or via
// data segments, but it is not allowed to be in the dynamic table already.
assert(!NXHashMember(allocatedClasses, cls));
if (!isKnownClass(cls))
NXHashInsert(allocatedClasses, cls);
if (addMeta)
addClassTableEntry(cls->ISA(), false);
}
因为当前的类已经进行的初始化分配处理,已经有了地址,所以也要添加到allocatedClasses
表中。
至此,已经初始化的类都已添加到gdb_objc_realized_classes
和allocatedClasses
表中。
处理@selector
// 将所有SEL都注册到哈希表中,是另外一张哈希表
// Fix up @selector references
static size_t UnfixedSelectors;
{
mutex_locker_t lock(selLock);
for (EACH_HEADER) {
if (hi->isPreoptimized()) continue;
bool isBundle = hi->isBundle();
SEL *sels = _getObjc2SelectorRefs(hi, &count);
UnfixedSelectors += count;
for (i = 0; i < count; i++) {
const char *name = sel_cname(sels[i]);
// 注册SEL的操作
sels[i] = sel_registerNameNoLock(name, isBundle);
}
}
}
这里简单介绍一下就是把我们的方法注册到内存中,也就是我们调用写好的方法会有方法名的提示,其实就是在内存中获取,通过我们的方法名获取到sel
。
处理非懒加载的类
for (EACH_HEADER) {
classref_t *classlist =
_getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
// printf("non-lazy Class:%s\n",cls->mangledName());
if (!cls) continue;
// hack for class __ARCLite__, which didn't get this above
#if TARGET_OS_SIMULATOR
if (cls->cache._buckets == (void*)&_objc_empty_cache &&
(cls->cache._mask || cls->cache._occupied))
{
cls->cache._mask = 0;
cls->cache._occupied = 0;
}
if (cls->ISA()->cache._buckets == (void*)&_objc_empty_cache &&
(cls->ISA()->cache._mask || cls->ISA()->cache._occupied))
{
cls->ISA()->cache._mask = 0;
cls->ISA()->cache._occupied = 0;
}
#endif
addClassTableEntry(cls);
if (cls->isSwiftStable()) {
if (cls->swiftMetadataInitializer()) {
_objc_fatal("Swift class %s with a metadata initializer "
"is not allowed to be non-lazy",
cls->nameForLogging());
}
// fixme also disallow relocatable classes
// We can't disallow all Swift classes because of
// classes like Swift.__EmptyArrayStorage
}
// 实现所有非懒加载的类(实例化类对象的一些信息,例如rw)
realizeClassWithoutSwift(cls);
}
}
这段代码三个重要点:
-
remapClass
将类进行重映射 -
addClassTableEntry
将类插入到allocatedClasses
表中,如果存在就不插入 -
realizeClassWithoutSwift
实现所有非懒加载的类(实例化类对象的一些信息,例如rw)
接下来我们就看一下realizeClassWithoutSwift
具体实现的什么
realizeClassWithoutSwift
探索
/***********************************************************************
* realizeClassWithoutSwift
* Performs first-time initialization on class cls,
* including allocating its read-write data.
* Does not perform any Swift-side initialization.
* Returns the real class structure for the class.
* Locking: runtimeLock must be write-locked by the caller
**********************************************************************/
// ✅注释:对类cls执行首次初始化,包括分配读写数据。不执行任何Swift端初始化。返回类的实际类结构
static Class realizeClassWithoutSwift(Class cls)
{
runtimeLock.assertLocked();
// ✅初始化类中的 ro、rw、父类和元类
const class_ro_t *ro;
class_rw_t *rw;
Class supercls;
Class metacls;
bool isMeta;
// ✅对类进行判断,下面有递归,isa的经典走位图,最终父类和元类指向nil
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?
// ✅读取cls中的data数据,对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 {// ✅当前的类,读取cls中的data数据,对ro进行赋值(编译时已经赋值),但是rw并没有赋值
// 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) {...}// 一些打印
// ✅remapClass源码,我们可以知道,remapClass主要是对类在表中进行查找的操作,如果表中已有该类,则返回一个空值,如果没有,则返回当前类,这样保证了类只加载一次,并结束递归归
supercls = realizeClassWithoutSwift(remapClass(cls->superclass));
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()));
...
// ✅ 将父类和元类赋值到类的父类和isa,这就是我们在lldb指令打印isa指向元类的原因
// 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();
}
}
// Propagate the associated objects forbidden flag from ro or from
// the superclass.
if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
(supercls && supercls->forbidsAssociatedObjects()))
{
rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
}
// ✅双向链表指向关系 父类中可以找到子类 子类中也可以找到父类
// Connect this class to its superclass's subclass lists
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
// Attach categories
methodizeClass(cls);
return cls;
}
下面进入methodizeClass
static void methodizeClass(Class cls)
{
runtimeLock.assertLocked();
bool isMeta = cls->isMetaClass();
auto rw = cls->data();// ✅对rw进行赋值,其实这里rw还是空的
auto ro = rw->ro;
// Install methods and properties that the class implements itself.
method_list_t *list = ro->baseMethods();// ✅读取ro里面的方法列表
if (list) {// ✅进行判断,有方法列表就把ro里面的baseMethods赋值给rw中的methods
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
rw->methods.attachLists(&list, 1);
}
property_list_t *proplist = ro->baseProperties;// ✅读取ro里面的属性列表
if (proplist) {// ✅进行判断,有属性列表就把ro里面的baseProperties赋值给rw中的properties
rw->properties.attachLists(&proplist, 1);
}
protocol_list_t *protolist = ro->baseProtocols;// ✅读取ro里面的协议列表
if (protolist) {// ✅进行判断,有属性列表就把ro里面的baseProtocols赋值给rw中的protocols
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 (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
}
ro
是在编译时就进行赋值的,只能读取,不能进行改变,rw
可以在进行调试的时候动态添加和处理方法、属性和协议。
上述把ro
里面的方法、属性和协议赋值给rw
的时候都是通过attachLists
进行添加,我们看一下attachLists
怎么执行的
attachLists
执行
void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;
if (hasArray()) {
// many lists -> many lists
uint32_t oldCount = array()->count;//10
uint32_t newCount = oldCount + addedCount;//4
setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
array()->count = newCount;// 10+4
memmove(array()->lists + addedCount, array()->lists,
oldCount * sizeof(array()->lists[0]));
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
else if (!list && addedCount == 1) {
// 0 lists -> 1 list
list = addedLists[0];
}
else {
// 1 list -> many lists
List* oldList = list;
uint32_t oldCount = oldList ? 1 : 0;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)malloc(array_t::byteSize(newCount)));
array()->count = newCount;
if (oldList) array()->lists[addedCount] = oldList;
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
}
可以看到attachLists
有三种方法:
-
0 lists -> 1 list
:直接通过addedLists
直接赋值 -
1 list -> many lists
: 将类开辟新的内存空间,将新数据进行存储 -
many lists -> many lists
:扩大现有容器的内存,将旧数据放在指定的位置,将需要增加的数据放在前面
总结
- 首先类的加载会先进行
_objc_init
函数的调用,最后来到_dyld_objc_notify_register
函数,传入map_images
-
map_images
执行的就是加载镜像文件,最终来到_read_images
读取镜像文件 - 在
_read_images
进行类存储到表gdb_objc_realized_classes
和allocatedClasses
中。对类进行重映射,把所有的sel
注册到nameSelectors
表中,将所有的Protocol
添加到protocol_map
表中。 - 存储类到表
gdb_objc_realized_classes
和allocatedClasses
是通过readClass
对类进行插入表的处理-
remapClass
将类进行重映射 -
addClassTableEntry
将类插入到allocatedClasses
表中,如果存在就不插入 -
realizeClassWithoutSwift
实现所有非懒加载的类(实例化类对象的一些信息,例如rw)
-
- 然后通过
realizeClassWithoutSwift
实现非懒加载类的一些信息相关的操作,如给类创建rw
结构,对父类,元类也进行相关初始化,对父类,元类,根类,子类进行相关的绑定 - 然后通过
methodizeClass
处理类的方法列表,协议列表,属性列表,将ro
的值赋给rw
- 同时也会将分类的信息进行加载。