OC的方法调用都是通过消息发送这种机制来实现的。当调用一个实例方法或者类方法时,底层实现是实例对象或者类对象调用objc_msgSend函数。先看个例子:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIView *view = [UIView new];
view.backgroundColor = [UIColor redColor];
}
// 下面是通过clang转换后的代码
static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
UIView *view = ((UIView *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("UIView"), sel_registerName("new"));
((void (*)(id, SEL, UIColor * _Nullable))(void *)objc_msgSend)((id)view, sel_registerName("setBackgroundColor:"), (UIColor * _Nullable)__null);
}
// 这是objc_msgSend函数的定义
objc_msgSend(void /* id self, SEL op, ... */ )
上面的代码可以看到,类方法和实例方法的调用都是通过调用objc_msgSend实现的。
OC是一门动态语言,调用的方法只要在.h文件声明了就能编译通过,但方法具体的调用要到运行时才能确定。objc_msgSend是用汇编实现的, objc_msgSend会再调用__class_lookupMethodAndLoadCache3函数, __class_lookupMethodAndLoadCache3函数调用lookUpImpOrForward函数,源码如下:
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
Class curClass;
IMP imp = nil;
Method meth;
bool triedResolver = NO;
runtimeLock.assertUnlocked();
// Optimistic cache lookup 缓存中查找
if (cache) {
imp = cache_getImp(cls, sel);
if (imp) return imp; //找到直接返回
}
if (!cls->isRealized()) {
rwlock_writer_t lock(runtimeLock);
realizeClass(cls);
}
if (initialize && !cls->isInitialized()) {
_class_initialize (_class_getNonMetaClass(cls, inst));
}
retry:
runtimeLock.read();
// Ignore GC selectors
if (ignoreSelector(sel)) {
imp = _objc_ignored_method;
cache_fill(cls, sel, imp, inst);
goto done;
}
// Try this class's cache. 不知道什么原因再次查缓存
imp = cache_getImp(cls, sel);
if (imp) goto done;
// Try this class's method lists. //类的方法列表中查找
meth = getMethodNoSuper_nolock(cls, sel);
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, cls);//找到缓存的类的缓存列表
imp = meth->imp;
goto done;
}
// Try superclass caches and method lists.//循环查询父类的缓存和方法列表中,一直找到NSObject为止
curClass = cls;
while ((curClass = curClass->superclass)) {
// 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.
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.
if (resolver && !triedResolver) {
runtimeLock.unlockRead();
_class_resolveMethod(cls, sel, inst);
// 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);
done:
runtimeLock.unlockRead();
// paranoia: look for ignored selectors with non-ignored implementations
assert(!(ignoreSelector(sel) && imp != (IMP)&_objc_ignored_method));
// paranoia: never let uncached leak out
assert(imp != _objc_msgSend_uncached_impcache);
return imp;
}
从上面的源码中可以看出,一个方法的调用可能会经过三个阶段:
-
消息查找阶段
很多人称为消息发送阶段,我自己更习惯称消息的查找阶段。在Class的结构及方法缓存中说过,方法的调用会先去类的缓存列表区查找函数的实现,从上面的源码也证实了这一点,如果在类的缓存中找到函数实现直接返回该实现。如果缓存中没有,接着去类的方法列表中查找,找后缓存到类中后返回该函数。
如果类方法列表也没有找到,接着去循环查询父类的缓存和方法列表,直至找NSObject为止。同样的,找后缓存到类中后返回该函数。如果都没有找到对应的方法实现,将进入第二个阶段,消息的动态解析;
-
消息动态解析阶段
之所以叫动态解析阶段,是因为我们可以在这个阶段为类或元类添加目标方法的实现。首先调用函数_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);
}
}
}
根据cls是类还是元类(也即是调用的是实例方法还是类方法)分别调用函数_class_resolveInstanceMethod、_class_resolveClassMethod添加目标方法的实现。这两个函数的实现大致相同,贴出_class_resolveInstanceMethod的源码:
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);
// 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));
}
}
}
如果没有在方法resolveInstanceMethod中为类添加对应的方法实现函数直接return。不论是否填加了实现,都会把结果缓存起来,避免以后调用再次触发方法的动态解析。如果在动态方法解析阶段,也没有为类添加方法实现,最后将进入消息转发阶段;
- 消息转发阶段
消息转发的具体的实现并没有开源,但是国外的开发者者根据调试的结果给出了消息转发的伪代码,如下:
// 消息转发伪代码
int __forwarding__(void *frameStackPointer, int isStret) {
id receiver = *(id *)frameStackPointer;
SEL sel = *(SEL *)(frameStackPointer + 8);
const char *selName = sel_getName(sel);
Class receiverClass = object_getClass(receiver);
// 调用 forwardingTargetForSelector:
if (class_respondsToSelector(receiverClass, @selector(forwardingTargetForSelector:))) {
id forwardingTarget = [receiver forwardingTargetForSelector:sel];
if (forwardingTarget && forwardingTarget != receiver) {
if (isStret == 1) {
int ret;
objc_msgSend_stret(&ret,forwardingTarget, sel, ...);
return ret;
}
return objc_msgSend(forwardingTarget, sel, ...);
}
}
// 僵尸对象
const char *className = class_getName(receiverClass);
const char *zombiePrefix = "_NSZombie_";
size_t prefixLen = strlen(zombiePrefix); // 0xa
if (strncmp(className, zombiePrefix, prefixLen) == 0) {
CFLog(kCFLogLevelError,
@"*** -[%s %s]: message sent to deallocated instance %p",
className + prefixLen,
selName,
receiver);
<breakpoint-interrupt>
}
// 调用 methodSignatureForSelector 获取方法签名后再调用 forwardInvocation
if (class_respondsToSelector(receiverClass, @selector(methodSignatureForSelector:))) {
NSMethodSignature *methodSignature = [receiver methodSignatureForSelector:sel];
if (methodSignature) {
BOOL signatureIsStret = [methodSignature _frameDescriptor]->returnArgInfo.flags.isStruct;
if (signatureIsStret != isStret) {
CFLog(kCFLogLevelWarning ,
@"*** NSForwarding: warning: method signature and compiler disagree on struct-return-edness of '%s'. Signature thinks it does%s return a struct, and compiler thinks it does%s.",
selName,
signatureIsStret ? "" : not,
isStret ? "" : not);
}
if (class_respondsToSelector(receiverClass, @selector(forwardInvocation:))) {
NSInvocation *invocation = [NSInvocation _invocationWithMethodSignature:methodSignature frame:frameStackPointer];
[receiver forwardInvocation:invocation];
void *returnValue = NULL;
[invocation getReturnValue:&value];
return returnValue;
} else {
CFLog(kCFLogLevelWarning ,
@"*** NSForwarding: warning: object %p of class '%s' does not implement forwardInvocation: -- dropping message",
receiver,
className);
return 0;
}
}
}
SEL *registeredSel = sel_getUid(selName);
// selector 是否已经在 Runtime 注册过
if (sel != registeredSel) {
CFLog(kCFLogLevelWarning ,
@"*** NSForwarding: warning: selector (%p) for message '%s' does not match selector known to Objective C runtime (%p)-- abort",
sel,
selName,
registeredSel);
} // doesNotRecognizeSelector
else if (class_respondsToSelector(receiverClass,@selector(doesNotRecognizeSelector:))) {
[receiver doesNotRecognizeSelector:sel];
}
else {
CFLog(kCFLogLevelWarning ,
@"*** NSForwarding: warning: object %p of class '%s' does not implement doesNotRecognizeSelector: -- abort",
receiver,
className);
}
// The point of no return.
kill(getpid(), 9);
}
从上面的代码可以看出消息转发阶段的整体流程:
-
首先调用forwardingTargetForSelector方法,我们可以在这个方法里返回一个实现了目标SEL的对象,那么最终会调用这个对象的方法实现。如果返回的对象没有实现目标函数,仍会报“unrecognized selector ...”的错误。
-
如果forwardingTargetForSelector中没有做处理,接着会调用methodSignatureForSelector方法,我们可以在这个方法里返回一个方法签名,返回方法签名后会调用forwardInvocation方法,在forwardInvocation方法里,我们可以任意的实现目标方法的调用。只要methodSignatureForSelector方法里返回了方法签名,forwardInvocation中的实现,我们可以任意处理,甚至不做任何处理程序都不会崩溃。
举个实例,新建Person类,只在.h文件中声明了两个方法classTest、instanceTest,但未实现。Student类声明的同样的方法,并在.m文件实现了这两个方法:
@interface Person : NSObject
+ (void)classTest;
- (void)instanceTest;
@end
@interface Student : NSObject
+ (void)classTest;
- (void)instanceTest;
@end
#import "Student.h"
@implementation Student
+ (void)classTest {
NSLog(@"%s",__func__);
}
- (void)instanceTest {
NSLog(@"%s",__func__);
}
@end
在Person的.m文件中没有这两个实现方法,但我们在forwardingTargetForSelector方法中返回一个实现了classTest、instanceTest的Student类对象和实例对象
#import "Person.h"
#import <objc/runtime.h>
#import "Student.h"
@implementation Person
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(instanceTest)) {
return [Student new];
}
return [super forwardingTargetForSelector:aSelector];
}
+ (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(classTest)) {
return [Student class];
}
return [super forwardingTargetForSelector:aSelector];
}
@end
上面代码运行打印结果如下:
-[Student instanceTest]
+[Student classTest]
在Person的.m文件中没有这两个实现方法,但我们在methodSignatureForSelector和forwardInvocation方法中做了处理:
#import "Person.h"
#import <objc/runtime.h>
#import "Student.h"
@implementation Person
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(instanceTest)) {
NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"v@:"];
return signature;
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
[anInvocation invokeWithTarget:[Student new]];
}
+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(classTest)) {
NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"v@:"];
return signature;
}
return [super methodSignatureForSelector:aSelector];
}
+ (void)forwardInvocation:(NSInvocation *)anInvocation {
[anInvocation invokeWithTarget:[Student class]];
}
@end
上面代码运行打印结果如下:
-[Student instanceTest]
+[Student classTest]
super的实现
在一段代码中[super viewDidLoad]这句代码通过clang编译后的代码如下:
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
去掉强制转换简化后的结构如下:
objc_msgSendSuper(
{
(id)self,
(id)class_getSuperclass(objc_getClass("ViewController"))
},
sel_registerName("viewDidLoad")
);
super的底层结构如下:
struct objc_super {
__unsafe_unretained id receiver;
__unsafe_unretained Class super_class;
};
super底层转换成调用objc_msgSendSuper函数,这个函数有两个参数,第一个参数为objc_super类型的对象,第二个参数是方法选择器;
下面是runtime源码中objc_msgSendSuper的定义
/**
* Sends a message with a simple return value to the superclass of an instance of a class.
(向实例对象的类的父类发送消息,并返回一个值)
*
* @param super A pointer to an \c objc_super data structure. Pass values identifying the
* context the message was sent to, including the instance of the class that is to receive the
* message and the superclass at which to start searching for the method implementation.
(super 是一个指向objc_super类型的对象的指针,根据所传的值确定消息发送具体来龙去脉,类的实例对象是消息接受者,superclass是指我们要从哪里开始查找方法的实现)
* @param op A pointer of type SEL. Pass the selector of the method that will handle the message.
* @param ...
* A variable argument list containing the arguments to the method.
*
* @return The return value of the method identified by \e op.
*
* @see objc_msgSend
*/
OBJC_EXPORT id _Nullable
objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
我给注释做了一些翻译,结合注释我们可以知道[super viewDidLoad]这句的代码的意思是:
- ViewController的实例( (id)self )是消息接受者;
- 从ViewController的父类( (id)class_getSuperclass(objc_getClass("ViewController")) )开始查找消息sel_registerName("viewDidLoad")的实现。
这里两个重点:消息的接受者是当前类的实例对象,从父类而不是当前类开始查找消息的实现;
网友评论