iOS 全网最新objc4 可调式/编译源码
编译好的源码的下载地址
序言
前面的文章中探究了类的结构,知道了类中都有哪些内容,那么今天就来探究一下,类到底是怎么加载进内存的呢?在什么时候加载到内存的呢?
我们定义的类.h
和.m
文件,首先需要通过编译器生产可执行文件,这个过程称为编译阶段
,然后安装在设备上加载运行。
编译
- 预编译:编译之前的一些先前的处理工作,处理一些
#
开头的文件,#include
和#define
以及条件编译
等;- 编译:对预编译后的文件进行
词法分析
、语法分析
和语义分析
,并进行代码优化,生成汇编代码;- 汇编:将汇编文件代码转换为机器可以执行的指令,并生成目标文件
.o
;- 链接:将所有
目标文件
以及链接的第三方库
,链接成可执行文件macho
;这一过程中,链接器将不同的目标文件链接起来,因为不同的目标文件之间可能有相互引用的变量或调用的函数,比如我们常用的系统库。
动态库与静态库
- 静态库:链接阶段将汇编生成的目标文件和引用库一起链接打包到可执行文件中,如:
.a
、.lib
。- 优点:编译成功后可执行文件可以独立运行,不需要依赖外部环境;
- 缺点:编译的文件会变大,如果静态库更新必须重新编译;
- 动态库:链接时不复制,程序运行时由系统加载到内存中,供系统调用,如:
.dylib
、.framework
。- 优点:系统只需加载一次,多次使用,共用节省内存,通过更新动态库,达到更新程序的目的;
- 缺点:可执行文件不可以单独运行,必须依赖外部环境;
系统的framework是动态的,开发者创建的framework是静态的
dyld
动态链接器
dyld
是iOS操作系统的一个重要组成部分,在系统内核做好程序准备工作之后,会交由dyld
负责余下的工作。dyld
的作用:加载各个库,也就是image
镜像文件,由dyld
从内存中读到表中,加载主程序,link
链接各个动静态库,进行主程序的初始化工作。
dyld
负责链接、加载程序,但是dyld
的探索过程比较繁琐就不详细展开了,直接进入类的加载
核心_objc_init
方法探索。
_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(); // 关于线程key的绑定
static_init(); // 运行C++静态构造函数
runtime_init(); // runtime运行时环境初始化
exception_init();// 初始化libobjc库的异常处理
#if __OBJC2__
cache_t::init(); // 缓存条件初始化
#endif
_imp_implementationWithBlock_init(); // 启动回调机制
// 注册处理程序,以便在映射、取消映射和初始化objc图像时调用,仅供运行时Runtime使用
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
#if __OBJC2__
didCallDyldNotifyRegister = **true**;
#endif
}
environ_init环境变量
/***********************************************************************
* environ_init
* Read environment variables that affect the runtime.
* Also print environment variable help, if requested.
************************************************************************ ** **/
void environ_init(void)
{
// 部分核心代码
// 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 (opt->internal
// && !os_variant_allows_internal_security_policies("com.apple.obj-c"))
// continue;
if (PrintHelp) _objc_inform("%s: %s", opt->env, opt->help);
if (PrintOptions && *opt->var) _objc_inform("%s is set", opt->env);
}
}
}
通过控制PrintHelp
和PrintOptions
可以打印当前环境变量的配置信息,我们在源码环境中把for
循环代码复制出来改一下,运行
也可以通过终端命令export OBJC_HELP = 1
,在终端上显示
可以通过Edit shceme
,在Environment Variables
配置相关变量
OBJC_DISABLE_NONPOINTER_ISA
:isa
的优化开关,如果YES
表示不使用,就是存指针;如果NO
开启指针优化,为nonpointer isa
;OBJC_PRINT_LOAD_METHODS
:是否开启打印所有load
方法,可以判断哪些类使用了load
方法,做相应的优化处理,以优化启动速度;
tls_init线程key的绑定
tls_init
关于线程key
的绑定,比如每个线程数据的析构函数
void tls_init(void)
{
#if SUPPORT_DIRECT_THREAD_KEYS
pthread_key_init_np(TLS_DIRECT_KEY, &_objc_pthread_destroyspecific);
#else
_objc_pthread_key = tls_create(&_objc_pthread_destroyspecific);
#endif
}
static_init
运行C++
静态构造函数
运行C++
静态构造函数。
libc
在dyld
调用静态构造函数之前调用objc_init()
,因此我们必须自己执行。
static void static_init()
{
size_t count1;
auto inits = getLibobjcInitializers(&_mh_dylib_header, &count1);
for (size_t i = 0; i < count1; i++) {
inits[i]();
}
size_t count2;
auto offsets = getLibobjcInitializerOffsets(&_mh_dylib_header, &count2);
for (size_t i = 0; i < count2; i++) {
UnsignedInitializer init(offsets[i]);
init();
}
#if DEBUG
if (count1 == 0 && count2 == 0)
_objc_inform("No static initializers found in libobjc. This is unexpected for a debug build. Make sure the 'markgc' build phase ran on this dylib. This process is probably going to crash momentarily due to using uninitialized global data.");
#endif
}
runtime_init
运行时环境初始化
Runtime
运行时环境初始化,主要是unattachedCategories
和allocatedClasses
两张表的初始化
void runtime_init(void)
{
objc::disableEnforceClassRXPtrAuth = DisableClassRXSigningEnforcement;
objc::unattachedCategories.init(32); // 分类表
objc::allocatedClasses.init(); // 已开辟类的表
}
exception_init
异常系统初始化
初始化libobjc
的异常处理系统,由map_images()
调用。注册异常处理的回调,从而监控异常的处理
void exception_init(void)
{
old_terminate = std::set_terminate(&_objc_terminate);
}
异常处理系统初始化后,当程序运行不符合底层规则
时,比如:数组越界
、方法未实现
等,系统就会发出异常信号。
有异常发生时,uncaught_handler
函数会把异常信息e
抛出
uncaught_handler
就是这里传进来的fn
,这个fn
就是我们检测异常的句柄
。我们可以自定义异常处理类,通过NSSetUncaughtExceptionHandler
把我们句柄
函数地址传进去
有异常发生时,系统会回调给我们异常exception
,然后自定义上传等处理操作。
cache_t::init
缓存条件初始化
void cache_t::init()
{
#if HAVE_TASK_RESTARTABLE_RANGES
mach_msg_type_number_t count = 0;
kern_return_t kr;
while (objc_restartableRanges[count].location) {
count++;
}
kr = task_restartable_ranges_register(mach_task_self(),
objc_restartableRanges, count);
if (kr == KERN_SUCCESS) return;
_objc_fatal("task_restartable_ranges_register failed (result 0x%x: %s)",
kr, mach_error_string(kr));
#endif // HAVE_TASK_RESTARTABLE_RANGES
}
_imp_implementationWithBlock_init
通常情况下,这没有任何作用,因为所有的初始化都是惰性的,但对于某些进程,我们急切地加载trampolines dylib。
在某些过程中急切地加载libobjc-tropolines.dylib。一些程序(最著名的是早期版本的嵌入式Chromium使用的QtWebEngineProcess)启用了一个限制性很强的沙盒配置文件,该文件阻止对该dylib的访问。如果有任何东西调用imp_implementationWithBlock(正如AppKit已经开始做的那样),那么我们将在尝试加载它时崩溃。在这里加载它会在启用沙盒配置文件并阻止它之前设置它。
void
_imp_implementationWithBlock_init(void)
{
#if TARGET_OS_OSX
// Eagerly load libobjc-trampolines.dylib in certain processes. Some
// programs (most notably QtWebEngineProcess used by older versions of
// embedded Chromium) enable a highly restrictive sandbox profile which
// blocks access to that dylib. If anything calls
// imp_implementationWithBlock (as AppKit has started doing) then we'll
// crash trying to load it. Loading it here sets it up before the sandbox
// profile is enabled and blocks it.
//
// This fixes EA Origin (rdar://problem/50813789)
// and Steam (rdar://problem/55286131)
if (__progname &&
(strcmp(__progname, "QtWebEngineProcess") == 0 ||
strcmp(__progname, "Steam Helper") == 0)) {
Trampolines.Initialize();
}
#endif
}
_dyld_objc_notify_register
void
_dyld_objc_notify_register(_dyld_objc_notify_mapped mapped,
_dyld_objc_notify_init init,
_dyld_objc_notify_unmapped unmapped);
_dyld_objc_notify_register
中的三个参数含义如下
&map_images
:dyld
将image
加载到内存中会调用该函数load_images
:dyld
初始化所有的image
文件会调用unmap_image
:将image
移除时会调用
我们重点看的就是将image
加载到内存中调用的函数map_image
在map_image
中调用map_images_nolock
map_images_nolock
中的代码比较多,我们这里直接看重点_read_images
_read_images
解读
在_read_images
方法中有360行代码,有点长,把里面的大括号
折叠,苹果的代码流程和注释是很好的,可以先整体把握一下,里面的ts.log
很清晰的告诉了我们整个流程。
void _read_images(header_info hList, uint32_t hCount, int
totalClasses, int
unoptimizedTotalClasses)
{
... //表示省略部分代码
#define EACH_HEADER \
hIndex = 0; \
hIndex < hCount && (hi = hList[hIndex]); \
hIndex++
// 条件控制进行一次的加载
if (!doneOnce) { ... }
// 修复预编译阶段的`@selector`的混乱的问题
// 就是不同类中有相同的方法 但是相同的方法地址是不一样的
// Fix up @selector references
static size_t UnfixedSelectors;
{ ... }
ts.log("IMAGE TIMES: fix up selector references");
// 错误混乱的类处理
// Discover classes. Fix up unresolved future classes. Mark bundle classes.
bool hasDyldRoots = dyld_shared_cache_some_image_overridden();
for (EACH_HEADER) { ... }
ts.log("IMAGE TIMES: discover classes");
// 修复重映射一些没有被镜像文件加载进来的类
// Fix up remapped classes
// Class list and nonlazy class list remain unremapped.
// Class refs and super refs are remapped for message dispatching.
if (!noClassesRemapped()) { ... }
ts.log("IMAGE TIMES: remap classes");
#if SUPPORT_FIXUP
// 修复一些消息
// Fix up old objc_msgSend_fixup call sites
for (EACH_HEADER) { ... }
ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");
#endif
// 当类中有协议时:`readProtocol`
// Discover protocols. Fix up protocol refs.
for (EACH_HEADER) { ... }
ts.log("IMAGE TIMES: discover protocols");
// 修复没有被加载的协议
// Fix up @protocol references
// Preoptimized images may have the right
// answer already but we don't know for sure.
for (EACH_HEADER) { ... }
ts.log("IMAGE TIMES: fix up @protocol references");
// 分类的处理
// Discover categories. Only do this after the initial category
// attachment has been done. For categories present at startup,
// discovery is deferred until the first load_images call after
// the call to _dyld_objc_notify_register completes.
if (didInitialAttachCategories) { ... }
ts.log("IMAGE TIMES: discover categories");
// 类的加载处理
// Category discovery MUST BE Late to avoid potential races
// when other threads call the new category code befor
// this thread finishes its fixups.
// +load handled by prepare_load_methods()
// Realize non-lazy classes (for +load methods and static instances)
for (EACH_HEADER) { ... }
ts.log("IMAGE TIMES: realize non-lazy classes");
// 没有被处理的类,优化那些被侵犯的类
// Realize newly-resolved future classes, in case CF manipulates them
if (resolvedFutureClasses) { ... }
ts.log("IMAGE TIMES: realize future classes");
...
#undef EACH_HEADER
}
- 条件控制,进行一次加载;
- 修复预编译阶段的
@selecter
混乱问题;- 错误混乱的类处理;
- 修复重新映射一些没有被镜像文件加载进来的类;
- 修复一些
消息
;- 当类里面有协议的时候:
readProtocol
;- 修复没有被加载进来的协议;
- 分类处理;
- 类的加载处理;
- 没有被处理的类
doneOnce
加载一次
if (!doneOnce) {
doneOnce = YES;
launchTime = 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) {
if (hi->info()->containsSwift() &&
hi->info()->swiftUnstableVersion() < objc_image_info::SwiftVersion3)
{
DisableNonpointerIsa = true;
if (PrintRawIsa) {
_objc_inform("RAW ISA: disabling non-pointer isa because "
"the app or a framework contains Swift code "
"older than Swift 3.0");
}
break;
}
}
# endif
#endif
if (DisableTaggedPointers) {
disableTaggedPointers();
}
// 小对象地址混淆
initializeTaggedPointerObfuscator();
if (PrintConnecting) {
_objc_inform("CLASS: found %d classes during launch", totalClasses);
}
// namedClasses
// Preoptimized classes don't go in this table.
// 4/3 is NXMapTable's load factor
// 容量:总数 * 4 / 3
int namedClassesSize =
(isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
// 创建哈希表,用于存放所有的类
gdb_objc_realized_classes =
NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
ts.log("IMAGE TIMES: first time tasks");
}
通过对doneOnce
的判断,只会进来一次条件语句,这里主要处理对类表的开辟创建处理gdb_objc_realized_classes
。
修复@selecter
混乱问题
static size_t UnfixedSelectors;
{
mutex_locker_t lock(selLock);
for (EACH_HEADER) {
if (hi->hasPreoptimizedSelectors()) continue;
bool isBundle = hi->isBundle();
// 从macho文件中获取方法名列表
SEL *sels = _getObjc2SelectorRefs(hi, &count);
UnfixedSelectors += count;
for (i = 0; i < count; i++) {
const char *name = sel_cname(sels[i]);
// sel通过name从dyld中查找获取
SEL sel = sel_registerNameNoLock(name, isBundle);
if (sels[i] != sel) { // 修复地址,以dyld为准
sels[i] = sel;
}
}
}
}
ts.log("IMAGE TIMES: fix up selector references");
对sel
进行修复,因为从编译后的macho
读取的sel
地址不一定是真实的sel
地址,在这里做修复。
错误混乱的类处理
// Discover classes. Fix up unresolved future classes. Mark bundle classes.
bool hasDyldRoots = dyld_shared_cache_some_image_overridden();
for (EACH_HEADER) {
if (! mustReadClasses(hi, hasDyldRoots)) {
// Image is sufficiently optimized that we need not call readClass()
continue;
}
// 从macho中读取的类列表
classref_t const *classlist = _getObjc2ClassList(hi, &count);
bool headerIsBundle = hi->isBundle();
bool headerIsPreoptimized = hi->hasPreoptimizedClasses();
for (i = 0; i < count; i++) {
Class cls = (Class)classlist[i];
// 通过readClass,将cls的类名和地址做关联
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;
}
}
}
ts.log("IMAGE TIMES: discover classes");
- 通过
_getObjc2ClassList
从macho中读取的所有的类; - 遍历所有的类,通过
readClass
讲类的地址和类名关联;
popFutureNamedClass
返回已实现的类,我们添加的类未实现,这里if
语句不成立;mangledName
是有值的,调用addNamedClass
将name=>cls
添加到命名的非元类映射中。addClassTableEntry
:将类添加到所有类的表中。如果addMeta为true,则自动添加类的元类。- 返回已处理的类
可以看出readClass
函数是把传进来的cls
,重新映射
并添加cls
和其元类
到所有的类表
中。
修复重新映射的类
类列表和非懒加载类列表仍然未被添加。 类引用和父类引用被重新映射以用于消息调度。
if (!noClassesRemapped()) {
for (EACH_HEADER) {
Class *classrefs = _getObjc2ClassRefs(hi, &count);
for (i = 0; i < count; i++) {
remapClassRef(&classrefs[i]);
}
// fixme why doesn't test future1 catch the absence of this?
classrefs = _getObjc2SuperRefs(hi, &count);
for (i = 0; i < count; i++) {
remapClassRef(&classrefs[i]);
}
}
}
ts.log("IMAGE TIMES: remap classes");
修复一些消息
// Fix up old objc_msgSend_fixup call sites | 修复旧的objc_msgSend_fixup调用站点
for (EACH_HEADER) {
message_ref_t *refs = _getObjc2MessageRefs(hi, &count);
if (count == 0) continue;
if (PrintVtables) {
_objc_inform("VTABLES: repairing %zu unsupported vtable dispatch "
"call sites in %s", count, hi->fname());
}
for (i = 0; i < count; i++) {
// 内部将常用的alloc、objc_msgSend等函数指针进行注册,并fix为新的函数指针
fixupMessageRef(refs+i);
}
}
ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");
添加协议
当类里面有协议的时候,调用readProtocol
绑定协议
// Discover protocols. Fix up protocol refs.
for (EACH_HEADER) {
extern objc_class OBJC_CLASS_$_Protocol;
Class cls = (Class)&OBJC_CLASS_$_Protocol;
ASSERT(cls);
NXMapTable *protocol_map = protocols();
bool isPreoptimized = hi->hasPreoptimizedProtocols();
// Skip reading protocols if this is an image from the shared cache
// and we support roots
// Note, after launch we do need to walk the protocol as the protocol
// in the shared cache is marked with isCanonical() and that may not
// be true if some non-shared cache binary was chosen as the canonical
// definition
if (launchTime && isPreoptimized) {
if (PrintProtocols) {
_objc_inform("PROTOCOLS: Skipping reading protocols in image: %s",
hi->fname());
}
continue;
}
bool isBundle = hi->isBundle();
protocol_t * const *protolist = _getObjc2ProtocolList(hi, &count);
for (i = 0; i < count; i++) {
readProtocol(protolist[i], cls, protocol_map,
isPreoptimized, isBundle);
}
}
ts.log("IMAGE TIMES: discover protocols");
修复协议列表引用
上面做了协议和类的关联,这里是对协议进行重新映射
分类的处理
if (didInitialAttachCategories) {
for (EACH_HEADER) {
load_categories_nolock(hi);
}
}
ts.log("IMAGE TIMES: discover categories");
分类的流程是比较重要的,在《iOS底层之分类的加载》专讲一下。
类的加载处理
// Realize non-lazy classes (for +load methods and static instances)
// 实现非懒加载类(实现了+load或静态实例方法)
for (EACH_HEADER) {
// 通过_getObjc2NonlazyClassList获取所有非懒加载类
classref_t const *classlist = hi->nlclslist(&count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (!cls) continue;
// 再次添加到所有类表中,如果已添加就不会添加进去,确保整个结构都被添加
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
}
// 对类cls执行首次初始化,包括分配其读写数据。不执行任何Swift端初始化。
realizeClassWithoutSwift(cls, **nil**);
}
}
ts.log("IMAGE TIMES: realize non-lazy classes");
本文重点
- 调用
nlclslist
(里面是调用_getObjc2NonlazyClassList
)获取所有非懒加载(non-lazy)的类;- 循环实现,再次添加到所有的类表中,如果已添加就不会添加进去,确保整个结构都被添加;
- 调用
realizeClassWithoutSwift
对类cls
执行首次初始化,包括分配其读写数据;
通过realizeClassWithoutSwift
实现所有非懒加载类的第一次初始化,那我们就看realizeClassWithoutSwift
如何实现的
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
runtimeLock.assertLocked();
class_rw_t *rw;
Class supercls;
Class metacls;
if (!cls) return nil;
if (cls->isRealized()) {
// 验证已实现的类
validateAlreadyRealizedClass(cls);
return cls;
}
ASSERT(cls == remapClass(cls));
// fixme verify class is not in an un-dlopened part of the shared cache?
auto ro = cls->safe_ro();
auto isMeta = ro->flags & RO_META; // 是否元类
if (ro->flags & RO_FUTURE) { // future类,rw的data已经初始化
// This was a future class. rw data is already allocated.
rw = cls->data();
ro = cls->data()->ro();
ASSERT(!isMeta);
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// Normal class. Allocate writeable class data.
rw = objc::zalloc<class_rw_t>();
rw->set_ro(ro);
rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
cls->setData(rw);
}
cls->cache.initializeToEmptyOrPreoptimizedInDisguise();
#if FAST_CACHE_META
if (isMeta) cls->cache.setBit(FAST_CACHE_META);
#endif
// 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 %s%s",
cls->nameForLogging(), isMeta ? " (meta)" : "",
(**void***)cls, ro, cls->classArrayIndex(),
cls->isSwiftStable() ? "(swift)" : "",
cls->isSwiftLegacy() ? "(pre-stable swift)" : "");
}
// 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.
// This assumes that none of those classes have Swift contents,
// or that Swift's initializers have already been called.
// fixme that assumption will be wrong if we add support
// for ObjC subclasses of Swift classes.
// 实现父类和元类
supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
// 纯isa还是nonpointer isa
#if SUPPORT_NONPOINTER_ISA
if (isMeta) {
// Metaclasses do not need any features from non pointer ISA
// This allows for a faspath for classes in objc_retain/objc_release.
cls->setInstancesRequireRawIsa(); // 纯指针isa
} else {
// 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 && 0 == strcmp(ro->getName(), "OS_object"))
{
// hack for libdispatch et al - isa also acts as vtable pointer
hackedDispatch = true;
instancesRequireRawIsa = true;
}
else if (supercls && supercls->getSuperclass() &&
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) { // 纯指针isa
cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
}
}
// SUPPORT_NONPOINTER_ISA
#endif
// Update superclass and metaclass in case of remapping
cls->setSuperclass(supercls); // 设置superclass指向父类
cls->initClassIsa(metacls); // 设置isa指向元类
// 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, previously);
return cls;
}
initClassIsa
时会根据nonpointer
区别设置
- 先类判断是否已经实现,如果已实现通过
validateAlreadyRealizedClass
验证;- 未实现,
cls->setData(rw)
处理类的data
也就是rw
和ro
,初始化rw
并拷贝ro
数据到rw
中;- 将事件往上层传递,来实现
父类
以及元类
;- 判断
isa
指针类型,是纯指针还是nonpointer
指针,在设置isa
时有不同;cls->setSuperclass(supercls)
:设置superclass指向父类;cls->initClassIsa(metacls)
:设置isa指向元类;methodizeClass
:在这里进行方法、属性、协议、分类的实现;
看一下methodizeClass
的实现
static void methodizeClass(Class cls, Class previously)
{
runtimeLock.assertLocked();
bool isMeta = cls->isMetaClass();
auto rw = cls->data();
auto ro = rw->ro();
auto rwe = rw->ext();
// Methodizing for the first time
if (PrintConnecting) {
_objc_inform("CLASS: methodizing class '%s' %s",
cls->nameForLogging(), isMeta ? "(meta)" : "");
}
// Install methods and properties that the class implements itself.
// rwe:方法、属性、协议
method_list_t *list = ro->baseMethods;
if (list) {
// 写入方法,并对方法进行排序
prepareMethodLists(cls, &list, 1, **YES**, isBundleClass(cls), **nullptr**);
if (rwe) rwe->methods.attachLists(&list, 1);
}
property_list_t *proplist = ro->baseProperties;
if (rwe && proplist) {
rwe->properties.attachLists(&proplist, 1);
}
protocol_list_t *protolist = ro->baseProtocols;
if (rwe && protolist) {
rwe->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 根元类添加initialize初始化方法
addMethod(cls, @selector(initialize), (IMP)&objc_noop_imp, "", NO);
}
// Attach categories. // 分类处理
if (previously) {
if (isMeta) {
objc::unattachedCategories.attachToClass(cls, previously, ATTACH_METACLASS);
} else {
// When a class relocates, categories with class methods
// may be registered on the class itself rather than on
// the metaclass. Tell attachToClass to look for those.
objc::unattachedCategories.attachToClass(cls, previously, ATTACH_CLASS_AND_METACLASS);
}
}
objc::unattachedCategories.attachToClass(cls, cls,
isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
#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
}
methodizeClass
中主要就是对method
、property
、protocol
存放在rwe
的处理,其实这里的rwe
并没有值,因为还没有完成初始化,- 为根元类添加
initialize
初始化方法,- 对分类处理。
上面是非懒加载类
的处理,那么懒加载类
是在什么时候完成初始化的呢?根据懒加载原则
,应该就是在用的时候再去调用吧,那就验证一下
源码环境中,我们在methodizeClass
通过类名字加断点
先在LGTeacher
里面实现+load
方法,运行
这里是从
_objc_init
和_read_images
进入的
在LGTeacher
里面去掉+load
方法,运行
这里可以看到,整个流程是在main
函数里调用LGTeacher
的alloc
方法来的,通过objc_msgSend
消息查找流程的lookUpImpOrForward
走到了这里。
总结
类的加载通过类是否实现+load
或静态实例
方法,区分为懒加载类
和非懒加载类
非懒加载类
:是在启动时map_images
时加载进内存的,通过_getObjc2NonlazyClassList
得到所有非懒加载类
,循环调用realizeClassWithoutSwift
到methodizeClass
完成初始化。
懒加载类
:是在第一次消息发送
的时候,检查类是否初始化,为完成初始化再去完成初始化流程。
关于消息发送文章:
iOS底层之Runtime探索(一)
iOS底层之Runtime探索(二)
iOS底层之Runtime探索(三)
iOS 全网最新objc4 可调式/编译源码
编译好的源码的下载地址
分类加载的流程分析 《iOS底层之分类的加载》
以上是对iOS中类的加载
通过源码的探索过程,如有疑问或错误之处请留言或私信我