Runtime消息机制:
本质上讲,OC的每一次方法调度都是一次消息的发送。其中方法调度的原理如下:
/*****************************************************************
*
* id objc_msgSend(id self, SEL _cmd,...);
*
*****************************************************************/
ENTRY objc_msgSend
MESSENGER_START
cbz r0, LNilReceiver_f // 判断消息接收者是否为nil
ldr r9, [r0] // r9 = self->isa
CacheLookup NORMAL // 到缓存中查找方法
LCacheMiss: // 方法未缓存
MESSENGER_END_SLOW
ldr r9, [r0, #ISA]
b __objc_msgSend_uncached
LNilReceiver: // 消息接收者为nil处理
mov r1, #0
mov r2, #0
mov r3, #0
FP_RETURN_ZERO
MESSENGER_END_NIL
bx lr
LMsgSendExit:
END_ENTRY objc_msgSend
一个方法的调度主要包括以下几个步骤:
没有找到缓存,到类的方法列表中依次寻找
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();
// 优先查找缓存
if (cache) {
imp = cache_getImp(cls, sel);
if (imp) return imp;
}
//runtimeLock在方法搜索期间保持,使方法查找+缓存填充原子相对于方法添加。否则添加一个类别,但是会无限期地忽略它,因为在代表类别的缓存刷新之后,缓存会用旧值重新填充
runtimeLock.lock();
checkIsKnownClass(cls);
if (!cls->isRealized()) {
cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
// runtimeLock may have been dropped but is now locked again
}
// 如果类未初始化,对其进行初始化。如果这个消息是initialize,那么直接进行类的初始化
if (initialize && !cls->isInitialized()) {
cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
}
retry:
runtimeLock.assertLocked();
// Try this class's cache.
// 遍历缓存方法,如果找到,直接返回
imp = cache_getImp(cls, sel);
if (imp) goto done;
// 从当前类的方法列表中查找
{
Method meth = getMethodNoSuper_nolock(cls, sel);
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, cls);
imp = meth->imp;
goto done;
}
}
// 沿着继承c链从父类的缓存查找,如果没查找到,从父类的方法列表中查找
{
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.");
}
// 父类缓存
imp = cache_getImp(curClass, sel);
if (imp) {
if (imp != (IMP)_objc_msgForward_impcache) {
//如果在父类方法中找到,,在当前类缓存列表中添加
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;
}
}
// 父类方法列表查找.
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
imp = meth->imp;
goto done;
}
}
}
// 没有找到任何的方法实现,进入消息转发第一阶段“动态方法解析”
// 调用+ (BOOL)resolveInstanceMethod: (SEL)selector
// 征询接收者所属的类是否能够动态的添加这个未实现的方法来解决问题
if (resolver && !triedResolver) {
runtimeLock.unlock();
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;
}
// 仍然没有找到方法实现进入消息转发第二阶段
//先后会调用 -(id)forwardingTargetForSelector: (SEL)selector
// 以及 - (void)forwardInvocation: (NSInvocation*)invocation 进行最后的补救
imp = (IMP)_objc_msgForward_impcache;
cache_fill(cls, sel, imp, inst);
done:
runtimeLock.unlock();
return imp;
}
备援的接收者
,就是找到一个接盘侠来处理这个事件消息转发
//本例中run为实例发放,walk为类方法
+ (BOOL)resolveInstanceMethod:(SEL)sel{
NSLog(@"%@",NSStringFromSelector(sel));
if(sel == @selector(run)){
class_addMethod([self class], sel, (IMP)class_getMethodImplementation([self class], @selector(testRun)), nil);
return YES;
}
return [super resolveClassMethod:sel];
}
+ (BOOL)resolveClassMethod:(SEL)sel{
NSLog(@"%@",NSStringFromSelector(sel));
if(sel == @selector(walk)){
bool success = class_addMethod(objc_getMetaClass([NSStringFromClass(self) UTF8String]), sel, (IMP)class_getMethodImplementation(objc_getMetaClass([NSStringFromClass(self) UTF8String]), @selector(testWalk)), "v@:");
// //"v@:“,按顺序分别表示:
// // v : v表示返回值为void
// // @ :参数id(self)
// // : :SEL(_cmd)对象
return YES;
}
return [super resolveClassMethod:sel];
}
//如果没有做上面两个添加方法操作,可以让其他对象来处理这个消息
- (id)forwardingTargetForSelector:(SEL)aSelector{
if([NSStringFromSelector(aSelector) isEqualToString:@"walk"]){
// return [[Tools alloc]init]; //实例方法返回
return [Tools class]; //类方法返回
}
return [super forwardingTargetForSelector:aSelector];
}
//methodSignatureForSelector需要和forwardInvocationf同时实现
//methodSignatureForSelector:和forwardInvocation:。methodSignatureForSelector:的作用在于为另一个类实现的消息创建一个有效的方法签名,必须实现,并且返回不为空的methodSignature,否则会crash。
//forwardInvocation:将选择器转发给一个真正实现了该消息的对象。
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
NSString *selStr = NSStringFromSelector(aSelector);
if ([selStr isEqualToString:@"run"]){
// return [NSMethodSignature signatureWithObjCTypes:nil];
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
// return [NSMethodSignature methodSignatureForSelector:(IMP)class_getMethodImplementation([self class], @selector(testRun))];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
//重新定义消息接收者 改变id
// [anInvocation invokeWithTarget:[[Tools alloc] init]];
//改变消息的sel
anInvocation.selector = @selector(testRun);
[anInvocation invokeWithTarget:self];
}
缓存查找
bucket_t * cache_t::find(SEL s, id receiver)
{
assert(s != 0);
bucket_t *b = buckets();
mask_t m = mask();
//cache_hash ( return (mask_t)(uintptr_t)sel & mask; //获取存储在散列表中的hash下标)
mask_t begin = cache_hash(s, m);
mask_t i = begin;
do {
//如果找到则返回
if (b[i].sel() == 0 || b[i].sel() == s) {
return &b[i];
}
//hash存在冲突则继续向下查找
} while ((i = cache_next(i, m)) != begin);
// hack
Class cls = (Class)((uintptr_t)this - offsetof(objc_class, cache));
cache_t::bad_cache(receiver, (SEL)s, cls);
}
当前类中的方法查找:
对于已排序好的列表,采用二分查找算法查找
没有排序的列表,采用遍历的方法查找
static method_t *search_method_list(const method_list_t *mlist, SEL sel)
{
int methodListIsFixedUp = mlist->isFixedUp();
int methodListHasExpectedSize = mlist->entsize() == sizeof(method_t);
if (__builtin_expect(methodListIsFixedUp && methodListHasExpectedSize, 1)) {
//采用二分查找
return findMethodInSortedMethodList(sel, mlist);
} else {
// Linear search of unsorted method list
//线性查找
for (auto& meth : *mlist) {
if (meth.name == sel) return &meth;
}
}
#if DEBUG
// sanity-check negative results
if (mlist->isFixedUp()) {
for (auto& meth : *mlist) {
if (meth.name == sel) {
_objc_fatal("linear search worked when binary search did not");
}
}
}
#endif
return nil;
}
文章部分内容参考:https://www.jianshu.com/p/f9cdaccc9f88