目前遗留的问题
- copy和strong修饰符的区别(objc_setProperty和内存平移, objc_getProperty都在什么情况下会调用)
- alloc的objc_alloc, objc_opt_class和objc_opt_isKindOfClass的符号查找
- 为什么第一次加载的时候firstSubclass=nil, 在执行或者调用了LGTeacher之后, 就会有firstSubclass=LGTeacher的赋值.
lookUpImpOrForward
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
const IMP forward_imp = (IMP)_objc_msgForward_impcache;
IMP imp = nil;
Class curClass;
runtimeLock.assertUnlocked();
if (slowpath(!cls->isInitialized())) {
// The first message sent to a class is often +new or +alloc, or +self
// which goes through objc_opt_* or various optimized entry points.
//
// However, the class isn't realized/initialized yet at this point,
// and the optimized entry points fall down through objc_msgSend,
// which ends up here.
//
// We really want to avoid caching these, as it can cause IMP caches
// to be made with a single entry forever.
//
// Note that this check is racy as several threads might try to
// message a given class for the first time at the same time,
// in which case we might cache anyway.
behavior |= LOOKUP_NOCACHE;
}
// runtimeLock is held during isRealized and isInitialized checking
// to prevent races against concurrent realization.
// runtimeLock is held during method search to make
// method-lookup + cache-fill atomic with respect to method addition.
// Otherwise, a category could be added but ignored indefinitely because
// the cache was re-filled with the old value after the cache flush on
// behalf of the category.
runtimeLock.lock();
// We don't want people to be able to craft a binary blob that looks like
// a class but really isn't one and do a CFI attack.
//
// To make these harder we want to make sure this is a class that was
// either built into the binary or legitimately registered through
// objc_duplicateClass, objc_initializeClassPair or objc_allocateClassPair.
checkIsKnownClass(cls);
cls = realizeAndInitializeIfNeeded_locked(inst, cls, behavior & LOOKUP_INITIALIZE);
// runtimeLock may have been dropped but is now locked again
runtimeLock.assertLocked();
curClass = cls;
// The code used to lookup the class's cache again right after
// we take the lock but for the vast majority of the cases
// evidence shows this is a miss most of the time, hence a time loss.
//
// The only codepath calling into this without having performed some
// kind of cache lookup is class_getInstanceMethod().
for (unsigned attempts = unreasonableClassCount();;) {
if (curClass->cache.isConstantOptimizedCache(/* strict */true)) {
#if CONFIG_USE_PREOPT_CACHES
imp = cache_getImp(curClass, sel);
if (imp) goto done_unlock;
curClass = curClass->cache.preoptFallbackClass();
#endif
} else {
// curClass method list.
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
imp = meth->imp(false);
goto done;
}
if (slowpath((curClass = curClass->getSuperclass()) == nil)) {
// No implementation found, and method resolver didn't help.
// Use forwarding.
imp = forward_imp;
break;
}
}
// Halt if there is a cycle in the superclass chain.
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.
// Stop searching, but don't cache yet; call method
// resolver for this class first.
break;
}
if (fastpath(imp)) {
// Found the method in a superclass. Cache it in this class.
goto done;
}
}
// No implementation found. Try method resolver once.
if (slowpath(behavior & LOOKUP_RESOLVER)) {
behavior ^= LOOKUP_RESOLVER;
return resolveMethod_locked(inst, sel, cls, behavior);
}
done:
if (fastpath((behavior & LOOKUP_NOCACHE) == 0)) {
#if CONFIG_USE_PREOPT_CACHES
while (cls->cache.isConstantOptimizedCache(/* strict */true)) {
cls = cls->cache.preoptFallbackClass();
}
#endif
log_and_fill_cache(cls, imp, sel, inst, curClass);
}
done_unlock:
runtimeLock.unlock();
if (slowpath((behavior & LOOKUP_NIL) && imp == forward_imp)) {
return nil;
}
return imp;
}
上个篇章讲到了, lookUpImpOrForward会有一个返回值imp存放在x17里面, 最终汇编会执行x17. 所以lookUpImpOrForward的返回值就是我们关注的.
if (slowpath(behavior & LOOKUP_RESOLVER)) {
behavior ^= LOOKUP_RESOLVER;
return resolveMethod_locked(inst, sel, cls, behavior);
}
return imp;
全局只有两个地方return, 所以我们只需要关注imp的赋值地方和resolveMethod_locked干嘛了就可以了.
imp的赋值
NEVER_INLINE
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
//forward_imp = _objc_msgForward_impcache
const IMP forward_imp = (IMP)_objc_msgForward_impcache;
IMP imp = nil;
Class curClass;
runtimeLock.assertUnlocked();
if (slowpath(!cls->isInitialized())) {
//如果cls没有Initialized那么就走这里
//LOOKUP_NOCACHE = 8, 1000, behavior = 3, 0011, 1011
behavior |= LOOKUP_NOCACHE;
}
runtimeLock.lock();
//检查当前的calss是否已经注册在表里
checkIsKnownClass(cls);
//检查类是否已经实现了, behavior只要是单数就标明类已经实现, 如果这个地方类没有实现, 则会在该方法内实现类.
cls = realizeAndInitializeIfNeeded_locked(inst, cls, behavior & LOOKUP_INITIALIZE);
// runtimeLock may have been dropped but is now locked again
// runtime的锁可能被删除了, 在这里再次上锁.
runtimeLock.assertLocked();
// cls赋值给curClass
curClass = cls;
//这是一个死循环
for (unsigned attempts = unreasonableClassCount();;) {
//如果有共享缓存, 且在共享缓存中找到了imp, 则直接跳转到done_unlock的代码处
if (curClass->cache.isConstantOptimizedCache(/* strict */true)) {
#if CONFIG_USE_PREOPT_CACHES
imp = cache_getImp(curClass, sel);
if (imp) goto done_unlock;
curClass = curClass->cache.preoptFallbackClass();
#endif
} else {
// curClass method list.
//根据当前类和sel 获取到Method, 在内部根据二分法查找方法
Method meth = getMethodNoSuper_nolock(curClass, sel);
//如果找到了方法, 跳转至done
if (meth) {
imp = meth->imp(false);
goto done;
}
//判断中将curClass的父类赋值curClass, 判断是否等于nil, 等于nil代表curClass是NSObject类, 方法未找到.
if (slowpath((curClass = curClass->getSuperclass()) == nil)) {
// No implementation found, and method resolver didn't help.
// Use forwarding.
//forward_imp赋值给imp, 跳出循环
imp = forward_imp;
break;
}
}
//此时的curClass已经是传入cls的父类了,
// Halt if there is a cycle in the superclass chain.
//如果--attempts==0成立, 那么标识内存损坏
if (slowpath(--attempts == 0)) {
_objc_fatal("Memory corruption in class list.");
}
// Superclass cache.
//获取父类的imp, cache_getImp为汇编, 只会寻找当前Class
的cache_t, 找不到则返回nil.
imp = cache_getImp(curClass, sel);
//如果imp等于forward_imp, 跳出循环
if (slowpath(imp == forward_imp)) {
// Found a forward:: entry in a superclass.
// Stop searching, but don't cache yet; call method
// resolver for this class first.
break;
}
//如果imp存在则跳转至done
if (fastpath(imp)) {
// Found the method in a superclass. Cache it in this class.
goto done;
}
}
// No implementation found. Try method resolver once.
//上文只有imp == forward_imp一种情况会break, 会走到这里 . 所以当前的imp就是forward_imp, uncache方法走入这里behavior = 3, LOOKUP_RESOLVER = 2, 0011 & 0010 = 2. 不为0
if (slowpath(behavior & LOOKUP_RESOLVER)) {
//behavior = 2 ^ 2 = 0
behavior ^= LOOKUP_RESOLVER;
//inst = 消息接收者, sel消息名字, cls当前类, behavior为0
//这里就进入了下一次, 动态方法决议
return resolveMethod_locked(inst, sel, cls, behavior);
}
done:
//behavior传入为3, LOOKUP_NOCACHE为8, 所以& = 0
if (fastpath((behavior & LOOKUP_NOCACHE) == 0)) {
//进入之后, 真机下CONFIG_USE_PREOPT_CACHES = 1
#if CONFIG_USE_PREOPT_CACHES
//从共享缓存中获取到对应的类
while (cls->cache.isConstantOptimizedCache(/* strict */true)) {
cls = cls->cache.preoptFallbackClass();
}
#endif
//然后进行cache.insert, 讲imp, sel插入缓存
log_and_fill_cache(cls, imp, sel, inst, curClass);
}
done_unlock:
runtimeLock.unlock();
//LOOKUP_NIL = 4, behavior =3 , & = 0, 一般不走
if (slowpath((behavior & LOOKUP_NIL) && imp == forward_imp)) {
return nil;
}
//返回imp
return imp;
}
下面是流程图:
报错_objc_msgForward_impcache流程
STATIC_ENTRY __objc_msgForward_impcache
// No stret specialization.
b __objc_msgForward
END_ENTRY __objc_msgForward_impcache
ENTRY __objc_msgForward
adrp x17, __objc_forward_handler@PAGE
ldr p17, [x17, __objc_forward_handler@PAGEOFF]
TailCallFunctionPointer x17
END_ENTRY __objc_msgForward
objc_runtime.mm
void *_objc_forward_handler = nil;
void *_objc_forward_stret_handler = nil;
#else
// Default forward handler halts the process.
__attribute__((noreturn, cold)) void
objc_defaultForwardHandler(id self, SEL sel)
{
_objc_fatal("%c[%s %s]: unrecognized selector sent to instance %p "
"(no message forward handler is installed)",
class_isMetaClass(object_getClass(self)) ? '+' : '-',
object_getClassName(self), sel_getName(sel), self);
}
void *_objc_forward_handler = (void*)objc_defaultForwardHandler;
总结
当我们在顺着类和父类的节奏, 一直找寻至NSObject的时候, 没有找到, 就会跳转出进入, 第一次behavior为3, LOOKUP_RESOLVER为2, 进入之后behavior异或之后变为1就再也不会进入了. 所以我们在和这个时候处理了resolveMethod_locked方法也会改变最终的程序执行结果. 下一篇看看resolveInstanceMethod.
if (slowpath(behavior & LOOKUP_RESOLVER)) {
behavior ^= LOOKUP_RESOLVER;
return resolveMethod_locked(inst, sel, cls, behavior);
}