罗列了 iOS 底层源码基础的比较有意思的十个函数/方法/结构。
No.1: search_method_list_inline
耳朵过来,我给你说个方法
`辣锅方法?`
你怎么知道?就是这个方法!
第一个查找方法的方法,名字就很魔幻,这个方法在各大流程里面几乎都有出现,在一个 entsize() 类型中寻找 sel 的方法,这个也是底层上关于二分法的一个应用
/***********************************************************************
* search_method_list_inline
**********************************************************************/
ALWAYS_INLINE static method_t *
findMethodInSortedMethodList(SEL key, const method_list_t *list)
{
ASSERT(list);
const method_t * const first = &list->first;
const method_t *base = first;
const method_t *probe;
uintptr_t keyValue = (uintptr_t)key;
uint32_t count;
for (count = list->count; count != 0; count >>= 1) {
probe = base + (count >> 1);
uintptr_t probeValue = (uintptr_t)probe->name;
if (keyValue == probeValue) {
// `probe` is a match.
// Rewind looking for the *first* occurrence of this value.
// This is required for correct category overrides.
while (probe > first && keyValue == (uintptr_t)probe[-1].name) {
probe--;
}
return (method_t *)probe;
}
if (keyValue > probeValue) {
base = probe + 1;
count--;
}
}
return nil;
}
No.2: .macro CacheLookup
别啥都问我、先寄几个儿看看行吗!?脑子 wa ta la?!
第二个是一个汇编宏,msgSend() 进入之后也就做了三件事情,第一判空,第二拿isa,第三就是下面这个方法,汇编实现的 macro CacheLookup
.macro CacheLookup
LLookupStart$1:
// p1 = SEL, p16 = isa --- #define CACHE (2 * __SIZEOF_POINTER__),其中 __SIZEOF_POINTER__表示pointer的大小 ,即 2*8 = 16
ldr p11, [x16, #CACHE] // p11 = mask|buckets 从x16(即isa)中平移16字节,取出cache 存入p11寄存器 --
//--- isa距离cache 正好16字节:isa(8字节)-superClass(8字节)-cache(mask高16位 + buckets低48位)
ldr p11, [x16, #CACHE]
//---- 64位真机
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
//--- p11(cache) & 0x0000ffffffffffff ,mask高16位抹零,得到buckets 存入p10寄存器-- 即去掉mask,留下buckets
and p10, p11, #0x0000ffffffffffff // p10 = buckets
//--- p11(cache)右移48位,得到mask(即p11 存储mask),mask & p1(msgSend的第二个参数 cmd-sel) ,
//--- 得到sel-imp的下标index(即搜索下标) 存入p12(cache insert写入时的哈希下标计算是 通过 sel & mask,读取时也需要通过这种方式)
and p12, p1, p11, LSR #48 // x12 = _cmd & mask
//--- 非64位真机
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
and p10, p11, #~0xf // p10 = buckets
and p11, p11, #0xf // p11 = maskShift
mov p12, #0xffff
lsr p11, p12, p11 // p11 = mask = 0xffff >> p11
and p12, p1, p11 // x12 = _cmd & mask
#else
#error Unsupported cache mask storage for ARM64.
#endif
//--- p12是下标 p10是buckets数组首地址,下标 * 1<<4(即16) 得到实际内存的偏移量,通过buckets的首地址偏移,获取bucket存入p12寄存器
//--- LSL #(1+PTRSHIFT)-- 实际含义就是得到一个bucket占用的内存大小 -- 相当于mask = occupied -1-- _cmd & mask -- 取余数
add p12, p10, p12, LSL #(1+PTRSHIFT)
// p12 = buckets + ((_cmd & mask) << (1+PTRSHIFT))
//--- 从x12(即p12)中取出 bucket 分别将imp和sel 存入 p17(存储imp) 和 p9(存储sel)
ldp p17, p9, [x12] // {imp, sel} = *bucket
//--- 比较 sel 与 p1(传入的参数cmd)
1: cmp p9, p1 // if (bucket->sel != _cmd)
//--- 如果不相等,即没有找到,请跳转至 2f
b.ne 2f // scan more
//--- 如果相等 即cacheHit 缓存命中,直接返回imp
CacheHit $0 // call or return imp
2: // not hit: p12 = not-hit bucket
//--- 如果一直都找不到, 因为是normal ,跳转至__objc_msgSend_uncached
CheckMiss $0 // miss if bucket->sel == 0
//--- 判断p12(下标对应的bucket) 是否 等于 p10(buckets数组第一个元素,),如果等于,则跳转至第3步
cmp p12, p10 // wrap if bucket == buckets
//--- 定位到最后一个元素(即第一个bucket)
b.eq 3f
//--- 从x12(即p12 buckets首地址)- 实际需要平移的内存大小BUCKET_SIZE,得到得到第二个bucket元素,imp-sel分别存入p17-p9,即向前查找
ldp p17, p9, [x12, #-BUCKET_SIZE]! // {imp, sel} = *--bucket
//--- 跳转至第1步,继续对比 sel 与 cmd
b 1b // loop
3: // wrap: p12 = first bucket, w11 = mask
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
add p12, p12, p11, LSR #(48 - (1+PTRSHIFT))
// p12 = buckets + (mask << 1+PTRSHIFT)
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
add p12, p12, p11, LSL #(1+PTRSHIFT)
// p12 = buckets + (mask << 1+PTRSHIFT)
#else
#error Unsupported cache mask storage for ARM64.
#endif
// Clone scanning loop to miss instead of hang when cache is corrupt.
// The slow path may detect any corruption and halt later.
//--- 再查找一遍缓存()
//--- 拿到x12(即p12)bucket中的 imp-sel 分别存入 p17-p9
ldp p17, p9, [x12] // {imp, sel} = *bucket
//--- 比较 sel 与 p1(传入的参数cmd)
1: cmp p9, p1 // if (bucket->sel != _cmd)
//--- 如果不相等,即走到第二步
b.ne 2f // scan more
//--- 如果相等 即命中,直接返回imp
CacheHit $0 // call or return imp
2: // not hit: p12 = not-hit bucket
//--- 如果一直找不到,则CheckMiss
CheckMiss $0 // miss if bucket->sel == 0
//--- 判断p12(下标对应的bucket) 是否 等于 p10(buckets数组第一个元素)-- 表示前面已经没有了,但是还是没有找到
cmp p12, p10 // wrap if bucket == buckets
b.eq 3f
//--- 从x12(即p12 buckets首地址)- 实际需要平移的内存大小BUCKET_SIZE,得到得到第二个bucket元素,imp-sel分别存入p17-p9,即向前查找
ldp p17, p9, [x12, #-BUCKET_SIZE]! // {imp, sel} = *--bucket
//--- 跳转至第1步,继续对比 sel 与 cmd
b 1b // loop
LLookupEnd$1:
LLookupRecover$1:
3: // double wrap
//--- 跳转至JumpMiss 因为是normal ,跳转至__objc_msgSend_uncached
JumpMiss $0
.endmacro
No.3: lookUpImpOrForward
找到了找到了,马上马上,我马上就到了。(赶紧下个单)
方法的慢速查找,贯穿了整个 runtime 的核心方法,它就是位于objc-runtime-new.mm文件中的 loopUpImpOrForward
方法:
/***********************************************************************
* lookUpImpOrForward.
* 标准的 IMP 查找流程的.
* 不包括 LOOKUP_INITIALIZE: 尝试去避免 +initialize (但是有时候会失败)
* 不包括 LOOKUP_CACHE: skips optimistic unlocked lookup (but uses cache elsewhere)
* 大多数调用者应该使用 LOOKUP_INITIALIZE 和 LOOKUP_CACHE
* inst 是 cls 或其子类的实例, 如果未知,则为nil。.
* 如果 cls 是未初始化的元类,则非 null 实例会更快。
* 可能会返回_objc_msgForward_impcache。
* 供外部使用的IMP必须转换为_objc_msgForward或_objc_msgForward_stret。
* 如果根本不想转发,请使用LOOKUP_NIL。
**********************************************************************/
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
// 首先上来就先给 forward_imp 做了一个赋值,这个赋值跟下去,是在汇编里面
// 实现体就是一个 b 指令调整到 objc_msgforward
const IMP forward_imp = (IMP)_objc_msgForward_impcache;
IMP imp = nil;
Class curClass;
runtimeLock.assertUnlocked();
// Optimistic cache lookup
if (fastpath(behavior & LOOKUP_CACHE)) {
imp = cache_getImp(cls, sel);
if (imp) goto done_nolock;
}
// runtimeLock在isRealized和isInitialized检查期间保持,
// 以防止与并发实现竞争。
// 因为考虑到有方法的添加,所以在方法的搜索过程中,
// 要添加 runtimeLock:用来保证 methodLookup 和 cacheFill 的原子性
// 否则,category 的方法可能在添加后永久地被忽略
// 因为有可能在category添加方法之后,旧方法马上进行了方法缓存
// (相当于以后每次都会去执行缓存的旧方法,不会找到category添加的新方法)
runtimeLock.lock();
// 我们不希望人们能够制作看起来像类但实际上不是一个二进制二进制Blob并进行CFI攻击。
//
// 为了使这些操作更困难,我们希望确保这是一个内置于二进制文件中或通过 objc_duplicateClass , objc_initializeClassPair 或 objc_allocateClassPair 合法注册的类。
//
// TODO: 在流程启动期间,此检查的成本很高。
checkIsKnownClass(cls);
if (slowpath(!cls->isRealized())) {
cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
// runtimeLock 可能已被删除,但现在再次被锁定
}
if (slowpath((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized())) {
cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
// runtimeLock 可能已被删除,但现在再次被锁定
// 如果sel == initialize,则class_initialize将发送+ initialize,然后在此过程完成后,Messenger将再次发送+ initialize。 当然,如果未从Messenger调用此命令,则不会发生。. 2778172
}
runtimeLock.assertLocked();
curClass = cls;
// 在我们获取锁之后,用于重新查找类的缓存的代码很快就出现了,
// 但是在绝大多数情况下,证据表明这在大多数情况下都是未命中的,因此会浪费时间。
//
// 没有执行某种缓存查找的唯一调用此方法的代码路径是 class_getInstanceMethod()。
for (unsigned attempts = unreasonableClassCount();;) {
// curClass method list.
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
imp = meth->imp;
goto done;
}
if (slowpath((curClass = curClass->superclass) == nil)) {
// 找不到实现,方法解析器也无济于事
// 使用 forwarding.
imp = forward_imp;
break;
}
// 如果 Superclass 链中存在循环,则停止。
if (slowpath(--attempts == 0)) {
_objc_fatal("Memory corruption in class list.");
}
// Superclass cache.
imp = cache_getImp(curClass, sel);
if (slowpath(imp == forward_imp)) {
// Found a forward:: entry in a superclass.
// 停止搜索,但不要缓存;请先调用此类的方法解析器。
break;
}
if (fastpath(imp)) {
// 在超类中找到了方法。在这个类中缓存它。
goto done;
}
}
// 找不到实现。尝试一次方法解析器。
if (slowpath(behavior & LOOKUP_RESOLVER)) {
behavior ^= LOOKUP_RESOLVER;
return resolveMethod_locked(inst, sel, cls, behavior);
}
done:
log_and_fill_cache(cls, imp, sel, inst, curClass);
runtimeLock.unlock();
done_nolock:
if (slowpath((behavior & LOOKUP_NIL) && imp == forward_imp)) {
return nil;
}
return imp;
}
No.4: realizeClassWithoutSwift
送你一个类!啥?你还要 swift 的?算了,给你 void 星吧
著名的创建 Class 的方法,在 loadimages 的时候被 dyld 调用。
另外,其实在上面也调用过它,就是第一次给懒加载的类发送消息的时候也调用过,大概就是这样的:
`lookUpImpOrForward`
--> `if(!cls->isRealized())`
--> `realizeClassMaybeSwiftAndLeaveLocked`
--> `realizeClassMaybeSwiftMaybeRelock`
--> `realizeClassWithoutSwift(cls, nil)`
/***********************************************************************
* realizeClassWithoutSwift
* 对类 cls 进行首次初始化,包括分配其 rw 的数据。
* 不执行任何Swift端初始化。
* 返回该类的真实类结构。
* 锁定:runtimeLock必须由调用方写锁定
**********************************************************************/
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
runtimeLock.assertLocked();
class_rw_t *rw;
Class supercls;
Class metacls;
if (!cls) return nil;
if (cls->isRealized()) return cls;
ASSERT(cls == remapClass(cls));
// fixme 验证类不是在共享高速缓存的未dlopened部分?
auto ro = (const class_ro_t *)cls->data();
auto isMeta = ro->flags & RO_META;
if (ro->flags & RO_FUTURE) {
// 这是将来的类结构. rw 以已经分配好了
rw = cls->data();
ro = cls->data()->ro();
ASSERT(!isMeta);
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// Normal class. 分配可写的类数据。
rw = objc::zalloc<class_rw_t>();
rw->set_ro(ro);
rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
cls->setData(rw);
}
#if FAST_CACHE_META
if (isMeta) cls->cache.setBit(FAST_CACHE_META);
#endif
// 选择此类的索引。
// 如果没有更多索引可用,则设置cls-> instancesRequireRawIsa
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)" : "");
}
// 接下来是实现超类和元类(如果尚未实现的话)。
// 对于根类,需要在上面设置了RW_REALIZED之后执行此操作。
// 对于根元类,需要在选择类索引之后执行此操作。
// 这假定这些类都不包含Swift内容,或者已经调用了Swift的初始化程序。
// fixme如果我们添加对Swift类的ObjC子类的支持,则假设将是错误的。
supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
#if SUPPORT_NONPOINTER_ISA
if (isMeta) {
// 元类不需要非指针 ISA 的任何功能
// 这样为类在 objc_retain/objc_release 提供便捷。
// This allows for a faspath for classes in objc_retain/objc_release.
cls->setInstancesRequireRawIsa();
} else {
// 在某些类和/或平台上禁用 nonPointer 的 isa。
// 设置instanceRequireRawIsa。
bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
bool rawIsaIsInherited = false;
static bool hackedDispatch = false;
if (DisableNonpointerIsa) {
// Non-pointer isa 被环境或应用程序SDK版本禁用
instancesRequireRawIsa = true;
}
else if (!hackedDispatch && 0 == strcmp(ro->name, "OS_object"))
{
// 对于LIbDebug等的黑客攻击——ISA也充当VTABLE指针
hackedDispatch = true;
instancesRequireRawIsa = true;
}
else if (supercls && supercls->superclass &&
supercls->instancesRequireRawIsa())
{
//这也由addSubclass()传播
//但非指针式isa安装程序需要更早。
//特殊情况:instancesRequireRawIsa不从根类传播到根元类
instancesRequireRawIsa = true;
rawIsaIsInherited = true;
}
if (instancesRequireRawIsa) {
cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
}
}
// SUPPORT_NONPOINTER_ISA
#endif
// 在重新映射时更新超类和元类
cls->superclass = supercls;
cls->initClassIsa(metacls);
// 协调实例变量偏移/布局。
// 这可能会重新分配 class_ro_t, 更新 ro 变量
if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro);
// 设置fastInstanceSize(如果尚未设置)。
cls->setInstanceSize(ro->instanceSize);
// 复制一些标志 from ro to rw
if (ro->flags & RO_HAS_CXX_STRUCTORS) {
cls->setHasCxxDtor();
if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
cls->setHasCxxCtor();
}
}
// 从ro或超类传播相关的objects forbidden标志。
if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
(supercls && supercls->forbidsAssociatedObjects()))
{
rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
}
// 将这个类连接到它的超类的子类列表
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
// 把 categorie 们加过来
methodizeClass(cls, previously);
return cls;
}
No.5: initializeNonMetaClass --> callInitialize(cls)
你加班能不能不唱歌?再唱我把你!再,我就,把你!婴内特!
是不是见过有人分析,什么时候调用 load 什么时候调用 initialize 方法,下面这个 initializeNonMetaClass 就是唯一一次调用 callInitialize(cls) 方法的方法,下面发出来两个方法:
initializeNonMetaClass:
/***********************************************************************
* class_initialize. 按需向任何未初始化的类发送“+initialize”消息。
* 首先强制初始化超类。
**********************************************************************/
void initializeNonMetaClass(Class cls)
{
ASSERT(!cls->isMetaClass());
Class supercls;
bool reallyInitialize = NO;
// 在开始初始化cls之前,确保super已完成初始化。
// 参见上面关于死锁的注释。
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
initializeNonMetaClass(supercls);
}
// 尝试原子操作设置 CLS_INITIALIZING.
SmallVector<_objc_willInitializeClassCallback, 1> localWillInitializeFuncs;
{
monitor_locker_t lock(classInitLock);
if (!cls->isInitialized() && !cls->isInitializing()) {
cls->setInitializing();
reallyInitialize = YES;
// 获取一个将在保持锁的情况下初始化函数的副本。
localWillInitializeFuncs.initFrom(willInitializeFuncs);
}
}
if (reallyInitialize) {
// 我们成功地设置了CLS_初始化位。初始化类。
// 记录我们正在初始化这个类,这样我们就可以给它发消息了。
_setThisThreadIsInitializingClass(cls);
if (MultithreadedForkChild) {
// LOL JK we don't really call +initialize methods after fork().
performForkChildInitialize(cls, supercls);
return;
}
for (auto callback : localWillInitializeFuncs)
callback.f(callback.context, cls);
// Send the +initialize message.
// 注意,这个 +initialize 是发送给superclass的,如果这个class没有实现 +initialize.2157218
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: calling +[%s initialize]",
objc_thread_self(), cls->nameForLogging());
}
// 异常:抛出异常的 +initialize 调用被视为完整且成功的 +initialize。
//
// 只有 __OBJC2__ 添加这些处理程序。 !__OBJC2__ 有一个引导问题与CF调用objc_exception_set_functions()相比。
#if __OBJC2__
@try
#endif
{
callInitialize(cls);
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: finished +[%s initialize]",
objc_thread_self(), cls->nameForLogging());
}
}
#if __OBJC2__
@catch (...) {
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: +[%s initialize] "
"threw an exception",
objc_thread_self(), cls->nameForLogging());
}
@throw;
}
@finally
#endif
{
// Done initializing.
lockAndFinishInitializing(cls, supercls);
}
return;
}
else if (cls->isInitializing()) {
//无法设置初始化,因为已设置初始化。
//如果此线程设置较早,则正常继续。
//如果其他线程设置了它,请在初始化完成之前阻塞它。
//如果在这里初始化更改为INITIALIZED是可以的,
//因为在阻塞之前,我们安全地检查锁内部是否已初始化。
if (_thisThreadIsInitializingClass(cls)) {
return;
} else if (!MultithreadedForkChild) {
waitForInitializeToComplete(cls);
return;
} else {
// 我们在fork()的子级,面对一个类,该类在fork()被调用时正被其他线程初始化。
_setThisThreadIsInitializingClass(cls);
performForkChildInitialize(cls, supercls);
}
}
else if (cls->isInitialized()) {
//Set CLS_INITIALIZING 失败,因为其他人已经初始化了该类。正常继续。
//注意:此检查必须在 IsInitialization 案例之后。
//否则:另一个线程正在初始化这个类, ISINITIALIZED 返回 false,跳过这个逻辑,然后另一个线程完成初始化并设置initialization=no和INITIALIZED=yes。
//跳过ISinitialization子句。可怕地死去。
return;
}
else {
// We shouldn't be here.
_objc_fatal("thread-safe class init in objc runtime is buggy!");
}
}
callInitialize(cls):
// 经典的 callInitialize 方法,就是往类里调用 initialize 方法
void callInitialize(Class cls)
{
((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize));
asm("");
}
No.6: _objc_Init
OC,我们重新开始吧?
嗯?你说的是从哪个新?DyldStart?ObjcInit?Main?
当然是你的 _objc_Init!
有没有经常看人家说main函数是程序的入口,那么main之前做了什么?
dyldStart
调用了dydldMian
initializeMainExecutable()
//上一行之后执行
objeInit
没错,这个 objcinit是真正进入了我们的objc项目,也就是我们常说的runtime 的层面才开始主责做的事情,虽然之前也调用了很多objc 的东西,但是大都是由dyld主要负责的。
这个方法很短:
/***********************************************************************
* _objc_init
* Bootstrap初始化。 用dyld注册我们的图像通知程序。
* 在库初始化之前由libSystem调用
**********************************************************************/
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();
runtime_init();
exception_init();
cache_init();
_imp_implementationWithBlock_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
#if __OBJC2__
didCallDyldNotifyRegister = true;
#endif
}
这里有一个车 _dyld_objc_notify_register
这里简单来说,dyld放了一个回调的方法,里面有我们dyly唤醒你之前发现的所有可行性文件里面的需要调用load 的地方,你醒来之后记得都去调用一下这些非懒加载的类。属于dyld和objcinit两个打个交叉配合,把初始化的流程全部搞起来。
No.7: dispatch_object_t
喂?组长你说GCD这源码咋写的这 sa* 内?
我们 GCD 的对象就不是对象吗?
第七点看这个 title : dispatch_object_t
,这个东西四舍五入就是一个 object_t
啊对不对?
其实可以说就是的,这个 dispatch_object_t
是 libspatch
里面的一个很基础的抽象基类
,注意抽象两个字,他不可以实例化,为什么呢?C一个基础语言,一个结构体为啥不能实例化?
用我们最熟悉的 ISA 来解释这个 dispatch_object_t
,说ISA是指针的其实说法是错的,ISA是一个联合体union,他要么是bits位域
类型,要么是isa指针
类型。你不能说一个实例化出来的ISA是一个不确定类型的联合体,哪怕人家是个空呢。
所以说我们看一下这个结构的定义:
他的定义分两部分,一部分是公开的,一部分是internal内部使用的。就简单看外部使用的,有兴趣的可以研究内部的所有对象。
/*!
* @typedef dispatch_object_t
*
* @abstract
* 所有分派对象的抽象基类型。
* 类型定义的详细信息是特定于语言的。specific.
*
* @discussion
* 通过调用 dispatch_retain() 和 dispatch_release() 来计算分派对象的引用数。
*/
typedef union {
struct _os_object_s *_os_obj;
struct dispatch_object_s *_do;
struct dispatch_queue_s *_dq;
struct dispatch_queue_attr_s *_dqa;
struct dispatch_group_s *_dg;
struct dispatch_source_s *_ds;
struct dispatch_channel_s *_dch;
struct dispatch_mach_s *_dm;
struct dispatch_mach_msg_s *_dmsg;
struct dispatch_semaphore_s *_dsema;
struct dispatch_data_s *_ddata;
struct dispatch_io_s *_dchannel;
} dispatch_object_t DISPATCH_TRANSPARENT_UNION;
这里面定义的每一行 struct 都是他的可能子类,这就是面向对象的多态
的实现,不需要父子类的继承,仅通过一个结构体的声明就可以完成!
No.8: thinking...
NFY:not finished yet:⑧⑨ 待补充,想写关联对象什么的没什么意思,runloop 的那个 dowhile 大循环没什么意思,我再思考思考🤔些什么比较有意思吧。
No.9: thinking...
No.10: longest method in objc
组长,我写的方法比这个长多了,你怎么不夸我呢?
我。。。
现在面试问的问题那么多底层的问题,我就不是很理解,万一对面的人回答不上来尴尬怎么办,我想到一个问题,既然要求候选人读源码,就问问候选人,
源码里你见过最长的一个方法是什么?
候选人可能就会觉得你这个公司特别的温暖...(什么鬼...)
这个方法就是最后一条,这个方法就是 dyld 的 main 方法...:
篇幅实在太长了,我就不粘贴出来了,因为一共六百多行!
dyld2.cpp,想看的话了这里看看
//
// Entry point for dyld. The kernel loads dyld and jumps to __dyld_start which
// sets up some registers and call this function.
//
// Returns address of main() in target program which __dyld_start jumps to
//
uintptr_t
_main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
int argc, const char* argv[], const char* envp[], const char* apple[],
uintptr_t* startGlue)
{
//............
//** 检查环境变量
// 把数组参数传递进函数体,然后for循环判断环境变量
{
//** 检查环境变量 // 把数组参数传递进函数体,然后for循环判断环境变量
checkEnvironmentVariables(envp);
//** 默认未初始化的后备路径
defaultUninitializedFallbackPaths(envp);
}
//............
// load shared cache 加载 shared cache 检查共享缓存区域是否开启可用
checkSharedRegionDisable((dyld3::MachOLoaded*)mainExecutableMH, mainExecutableSlide);
if ( gLinkContext.sharedRegionMode != ImageLoader::kDontUseSharedRegion ) {
#if TARGET_OS_SIMULATOR
if ( sSharedCacheOverrideDir)
mapSharedCache();
#else
mapSharedCache();
#endif
}
//............
// 从 ImageLoader 变成一个 「主要可执行文件」,
// 使用 instantiateFromLoadedImage 这个方法来生成
CRSetCrashLogMessage(sLoadingCrashMessage);
// instantiate ImageLoader for main executable
//从 ImageLoader 变成一个 「主要可执行文件」,使用 instantiateFromLoadedImage 这个方法来生成的
sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);
gLinkContext.mainExecutable = sMainExecutable;
gLinkContext.mainExecutableCodeSigned = hasCodeSignatureLoadCommand(mainExecutableMH);
//............
// 加载任何插入的库
if ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) {
for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib)
loadInsertedDylib(*lib);
}
// 记录插入的库的数量,以便统一搜索将先查看插入的库,然后是main,然后是其他。
sInsertedDylibCount = sAllImages.size()-1;
//............
// 链接 上面的那个 「主可执行文件」
gLinkContext.linkingMainExecutable = true;
#if SUPPORT_ACCELERATE_TABLES
if ( mainExcutableAlreadyRebased ) {
// 「主可执行文件」上的先前link()已为ASLR调整了其内部指针
//通过反基础变基来解决此问题
sMainExecutable->rebase(gLinkContext, -mainExecutableSlide);
}
#endif
//**!link sMainExecutable「就是主可执行文件」
link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);
sMainExecutable->setNeverUnloadRecursive();
//............
// 链接任何插入的库
// 在链接主可执行文件之后执行此操作,以便通过插入插入的任何dylib
// dylib(例如libSystem)不在程序使用的dylib之前
if ( sInsertedDylibCount > 0 ) {
for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
ImageLoader* image = sAllImages[i+1];
//**!link 动态库
link(image, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);
image->setNeverUnloadRecursive();
}
if ( gLinkContext.allowInterposing ) {
// 只允许动态库插入
// 绑定所有插入的库后,注册插入信息,以便链接工作
for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
ImageLoader* image = sAllImages[i+1];
image->registerInterposing(gLinkContext);
}
}
}
//............
//仅在所有插入的 image link 完成后 执行 「弱符号绑定」
// <rdar://problem/12186933> do weak binding only after all inserted images linked
// 仅在所有插入的 image link 完成后 执行 「弱符号绑定」
sMainExecutable->weakBind(gLinkContext);
gLinkContext.linkingMainExecutable = false;
//............
// 执行 所有的 initializers
initializeMainExecutable();
// initializeMainExecutable 的 for 循环的里面,都挨个执行 runInitializers()
//............
// 从Load Command读取LC_MAIN入口,如果没有,就读取LC_UNIXTHREAD
// 这里就是 Main 所谓的「程序的入口」
{
// 查找「主要可执行文件」的入口点
result = (uintptr_t)sMainExecutable->getEntryFromLC_MAIN();
if ( result != 0 ) {
// 主要可执行文件使用LC_MAIN,我们需要在libdyld中使用helper来调用main()
if ( (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 9) )
*startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit;
else
halt("libdyld.dylib support not present for LC_MAIN");
}
else {
// 主可执行文件使用LC_UNIXTHREAD,dyld需要在为main()设置的程序中“启动”
result = (uintptr_t)sMainExecutable->getEntryFromLC_UNIXTHREAD();
*startGlue = 0;
}
}
//............
}