1.消息查找
消息查找分为两部分:一部分是汇编,一部分是缓存查找部分。由于功底不够,本文介绍缓存查找部分。
message查找
MessageSend * send = [MessageSend new];
[send message];
直接在方法上加断点,单点跟踪是追踪不了的,我直接找到lookUpImpOrForward方法,在message断点后再打开断点。
_objc_msgSend_uncached是汇编的代码,
MethodTableLookup NORMAL调用了_class_lookupMethodAndLoadCache3
IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls)
{
return lookUpImpOrForward(cls, sel, obj,
YES/*initialize*/, NO/*cache*/, YES/*resolver*/);
}
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
IMP imp = nil;
bool triedResolver = NO;
runtimeLock.assertUnlocked();
// Optimistic cache lookup
if (cache) {
imp = cache_getImp(cls, sel);
if (imp) return imp;
}
runtimeLock.lock();
checkIsKnownClass(cls);
if (!cls->isRealized()) {//如果sel中的cls没有实现
realizeClass(cls);//实现cls
}
//用到才初始化
if (initialize && !cls->isInitialized()) {
runtimeLock.unlock();
_class_initialize (_class_getNonMetaClass(cls, inst));//初始化cls
runtimeLock.lock();
// If sel == initialize, _class_initialize will send +initialize and
// then the messenger will send +initialize again after this
// procedure finishes. Of course, if this is not being called
// from the messenger then it won't happen. 2778172
}
retry:
runtimeLock.assertLocked();
// Try this class's cache.
imp = cache_getImp(cls, sel);//汇编缓存查找:从cls缓存查找sel
if (imp) goto done;
// Try this class's method lists.
{
Method meth = getMethodNoSuper_nolock(cls, sel);//C语言查找:从methods中
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, cls);//方法缓存:找到存到缓存并返回
imp = meth->imp;
goto done;
}
}
// Try superclass caches and method lists.
// 父类查找
{
unsigned attempts = unreasonableClassCount();
for (Class curClass = cls->superclass;
curClass != nil;
curClass = curClass->superclass)
{
// Halt if there is a cycle in the superclass chain.
if (--attempts == 0) {
_objc_fatal("Memory corruption in class list.");
}
// Superclass cache.
imp = cache_getImp(curClass, sel);
if (imp) {
if (imp != (IMP)_objc_msgForward_impcache) {
// Found the method in a superclass. Cache it in this class.
log_and_fill_cache(cls, imp, sel, inst, curClass);
goto done;
}
else {
// Found a forward:: entry in a superclass.
// Stop searching, but don't cache yet; call method
// resolver for this class first.
break;
}
}
// Superclass method list.
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
imp = meth->imp;
goto done;
}
}
}
// No implementation found. Try method resolver once.
//方法没有实现,尝试resolver
if (resolver && !triedResolver) {
runtimeLock.unlock();
_class_resolveMethod(cls, sel, inst);
runtimeLock.lock();
// Don't cache the result; we don't hold the lock so it may have
// changed already. Re-do the search from scratch instead.
triedResolver = YES;
goto retry;
}
// No implementation found, and method resolver didn't help.
// Use forwarding.
//即没有实现,resolver也不能解决时
imp = (IMP)_objc_msgForward_impcache;//快速消息转发
cache_fill(cls, sel, imp, inst);
done:
runtimeLock.unlock();
return imp;
}
- 在当前类查找
cache为NO
cls->isRealized:判断cls是否实现,没有实现就实现
cls->isInitialized:判断cls是否初始化,没有初始化就初始化
cache_getImp:汇编查找:找到直接返回
getMethodNoSuper_nolock:从cls的data中查找
log_and_fill_cache:找到填充到bucket中 - 父类中查找
如果以上没找到:沿着superClass在父类查找 - 尝试解决
如还是没有找到:尝试用_class_resolveMethod查找
如果还没有实现:_objc_msgForward_impcache解决
查找流程:现在当前类查找,再在父类查找,找不到调用_class_resolveMethod方法,还没有解决会调用_objc_msgForward_impcache。其中只要有一步找到,就会立刻停止且方法添加到缓存中。
2. _class_resolveMethod
void _class_resolveMethod(Class cls, SEL sel, id inst)
{
if (! cls->isMetaClass()) {//实例方法
// try [cls resolveInstanceMethod:sel]
_class_resolveInstanceMethod(cls, sel, inst);
}
else {//类方法
// try [nonMetaClass resolveClassMethod:sel]
// and [cls resolveInstanceMethod:sel]
_class_resolveClassMethod(cls, sel, inst);
if (!lookUpImpOrNil(cls, sel, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/))
{
_class_resolveInstanceMethod(cls, sel, inst);
}
}
}
static void _class_resolveInstanceMethod(Class cls, SEL sel, id inst)
{
if (! lookUpImpOrNil(cls->ISA(), SEL_resolveInstanceMethod, cls,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/))
{
// Resolver not implemented.
return;
}
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(cls, SEL_resolveInstanceMethod, sel);//通过objc_msgSend调用类中的+ (BOOL)resolveInstanceMethod:(SEL)sel方法
// Cache the result (good or bad) so the resolver doesn't fire next time.
// +resolveInstanceMethod adds to self a.k.a. cls
IMP imp = lookUpImpOrNil(cls, sel, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/);
if (resolved && PrintResolving) {
if (imp) {
_objc_inform("RESOLVE: method %c[%s %s] "
"dynamically resolved to %p",
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel), imp);
}
else {
// Method resolver didn't add anything?
_objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES"
", but no new implementation of %c[%s %s] was found",
cls->nameForLogging(), sel_getName(sel),
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel));
}
}
}
static void _class_resolveClassMethod(Class cls, SEL sel, id inst)
{
assert(cls->isMetaClass());
if (! lookUpImpOrNil(cls, SEL_resolveClassMethod, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/))
{
// Resolver not implemented.
return;
}
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(_class_getNonMetaClass(cls, inst),
SEL_resolveClassMethod, sel);
// Cache the result (good or bad) so the resolver doesn't fire next time.
// +resolveClassMethod adds to self->ISA() a.k.a. cls
IMP imp = lookUpImpOrNil(cls, sel, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/);
if (resolved && PrintResolving) {
if (imp) {
_objc_inform("RESOLVE: method %c[%s %s] "
"dynamically resolved to %p",
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel), imp);
}
else {
// Method resolver didn't add anything?
_objc_inform("RESOLVE: +[%s resolveClassMethod:%s] returned YES"
", but no new implementation of %c[%s %s] was found",
cls->nameForLogging(), sel_getName(sel),
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel));
}
}
}
- 实例方法调用_class_resolveInstanceMethod,类方法调用_class_resolveClassMethod。
- _class_resolveClassMethod直接通过objc_msg_send 调用了resolveInstanceMethod方法
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(cls, SEL_resolveInstanceMethod, sel);//通过objc_msgSend调用类中的+ (BOOL)resolveInstanceMethod:(SEL)sel方法
- 类方法雷同,调用了resolveClassMethod方法。
而这两个方法是需要开发者自行处理的。
3.log_and_fill_cache
找到的方法填充到缓存
log_and_fill_cache(Class cls, IMP imp, SEL sel, id receiver, Class implementer)
{
#if SUPPORT_MESSAGE_LOGGING
if (objcMsgLogEnabled) {//log日志
bool cacheIt = logMessageSend(implementer->isMetaClass(),
cls->nameForLogging(),
implementer->nameForLogging(),
sel);
if (!cacheIt) return;
}
#endif
cache_fill (cls, sel, imp, receiver);
}
void cache_fill(Class cls, SEL sel, IMP imp, id receiver)
{
#if !DEBUG_TASK_THREADS
mutex_locker_t lock(cacheUpdateLock);
cache_fill_nolock(cls, sel, imp, receiver);
#else
_collecting_in_critical();
return;
#endif
}
//添加方法缓存
static void cache_fill_nolock(Class cls, SEL sel, IMP imp, id receiver)
{
cacheUpdateLock.assertLocked();
// Never cache before +initialize is done
if (!cls->isInitialized()) return;
// Make sure the entry wasn't added to the cache by some other thread
// before we grabbed the cacheUpdateLock.
if (cache_getImp(cls, sel)) return;
cache_t *cache = getCache(cls);//取出cls的cache:每一个cls都有一个cache_t
cache_key_t key = getKey(sel);
// Use the cache as-is if it is less than 3/4 full
mask_t newOccupied = cache->occupied() + 1;
mask_t capacity = cache->capacity();
if (cache->isConstantEmptyCache()) {
// Cache is read-only. Replace it.
cache->reallocate(capacity, capacity ?: INIT_CACHE_SIZE);//没有分配,分配内存
}
else if (newOccupied <= capacity / 4 * 3) {
// Cache is less than 3/4 full. Use it as-is.
}
else {
// Cache is too full. Expand it.
cache->expand();//缓存满了,扩展
}
// Scan for the first unused slot and insert there.
// There is guaranteed to be an empty slot because the
// minimum size is 4 and we resized at 3/4 full.
bucket_t *bucket = cache->find(key, receiver);
if (bucket->key() == 0) cache->incrementOccupied();
bucket->set(key, imp);//添加imp缓存
}
- cache_t *cache = getCache(cls);获取cls的cache_t
- 内存分配
- cache->isConstantEmptyCache(),cache->reallocate分配内存
- newOccupied小于3/4,不用操作
- newOccupied大于等于3/4,按原来2倍大小重新分配内存
- 设置bucket_t的缓存
小结:
- _objc_msgForward_impcache这个断点跟踪不了,没法看具体逻辑。
- 消息查找机制符合预期,可以思考下以下几个问题:
- 问题:如果父类与子类同时实现_class_resolveMethod,最后会走哪个?
_class_resolveMethod在父类查找后面实现,肯定是走父类方法不会走_class_resolveMethod - 问题2:父类中的实例方法self指的是子类的实例对象还是父类实例对象?
如果是子类有实现,self指的是子类的实例对象,而所有方法查找都是从子类先开始查找 - 问题3:父类的按钮有一个点击事件clickAction,而子类也实现了clickAction,最后调用的是哪个?
看对象是子类对象还是父类对象,如果是子类对象则调用的是子类clickAction,这也是模板模式的经典实用。把钩子留个子类
网友评论