OC调用方法
int main(int argc, const char * argv[]) {
@autoreleasepool {
Persion *persion = [[Persion alloc] init];
[persion teset];
}
return 0;
}
进行clang 成c++文件
//终端命令
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-cpp.cpp
结果如下
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
Persion *persion = ((Persion *(*)(id, SEL))(void *)objc_msgSend)((id)((Persion *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Persion"), sel_registerName("alloc")), sel_registerName("init"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)persion, sel_registerName("teset"));
}
return 0;
}
进行代码简化 结果如下
[persion teset];
objc_msgSend(persion, sel_registerName("teset"));
可以看到是执行的是objc_msgSend()
方法,第一个参数是消息接受者(receive)persion
, 第二个参数是方法签名sel_registerName("teset")
那么下面研究一下objc_msgSend()
方法
objc
最新版本(即版本号最大的)源码(源码下载地址)
找到如下_objc_msgSend
函数入口,可以看到这一段是用汇编写的
接下来我用图解走一下流程
最终走到了IMP lookUpImpOrForward(Class cls, SEL sel, id inst, bool initialize, bool cache, bool resolver)
这个函数
这个方法里面的重点看这一段代码 主要分3个阶段
- 第一个阶段
消息发送
retry: runtimeLock.assertLocked(); // Try this class's cache. //从这个类的方法缓存里面查找当前调用的方法 imp = cache_getImp(cls, sel); //如果查找到就返回函数地址imp if (imp) goto done; //如果从缓存里面没有找到, 就到当前这个类的类对象方法列表里面查找 // Try this class's method lists. { Method meth = getMethodNoSuper_nolock(cls, sel); if (meth) { //如果找到了,先缓存方法到类的方法缓存里面,再返回imp 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. 从curClass 类对象方法列表里面查找方法 Method meth = getMethodNoSuper_nolock(curClass, sel); if (meth) {//查到了缓存给当前类 并返回imp log_and_fill_cache(cls, meth->imp, sel, inst, curClass); imp = meth->imp; goto done; } } }
- 第二个阶段
动态解析
// No implementation found. Try method resolver once. 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. imp = (IMP)_objc_msgForward_impcache; cache_fill(cls, sel, imp, inst);
这是 goto done
是来到这里
done: runtimeLock.unlock(); return imp;
第一个阶段的就是在查找方法具体查找流程如图
_cache_getImp
这个函数也是汇编写的, 具体执行流程会执行到上图中的CacheLookup
函数
第二个阶段是动态解析
_class_resolveMethod(cls, sel, inst);
void _class_resolveMethod(Class cls, SEL sel, id inst)
{
if (! cls->isMetaClass()) {//不是元类
// try [cls resolveInstanceMethod:sel]
//这里会调用类方法的resolveInstanceMethod 方法
_class_resolveInstanceMethod(cls, sel, inst);
}
else {
// 如果是元类 , 则调用类方法的resolveClassMethod这个方法
// 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);
}
}
}
动态解析的具体代码实现 演示代码地址
- (void)other{
NSLog(@"%s",__func__);
}
//消息动态解析
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == @selector(test)) {
Method method = class_getInstanceMethod(self, @selector(other));
class_addMethod(self, sel, method_getImplementation(method), method_getTypeEncoding(method));
return YES;
}
return [super resolveInstanceMethod:sel];
}
第三个阶段是消息转发
_objc_msgForward_impcache
这个函数的调用
STATIC_ENTRY __objc_msgForward_impcache
// No stret specialization.
b __objc_msgForward
END_ENTRY __objc_msgForward_impcache
从这个会最终跟到__forwording__
就不开源了
消息转发的具体用法代码实现 演示代码地址
//消息转发
//第一种1.0
- (id)forwardingTargetForSelector:(SEL)aSelector
{
if (aSelector == @selector(test)) {
//返回值为nil,相当于没有实现这个方法
return [[Cat alloc] init];
}
return [super forwardingTargetForSelector:aSelector];
}
//第二种2.0
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
//返回方法签名
return [NSMethodSignature signatureWithObjCTypes:"v16@:"];
}
//NSInvocation 里面包含了: 方法调用者,方法名,参数
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
//修改方法调用者
anInvocation.target = [[Cat alloc] init];
//调用方法
[anInvocation invoke];
}