当前位置:   article > 正文

iOS-Runtime消息机制_ios 13执行sel

ios 13执行sel

Runtime消息机制:

       本质上讲,OC的每一次方法调度都是一次消息的发送。其中方法调度的原理如下:

  1. /*****************************************************************
  2. *
  3. * id objc_msgSend(id self, SEL _cmd,...);
  4. *
  5. *****************************************************************/
  6. ENTRY objc_msgSend
  7. MESSENGER_START
  8. cbz r0, LNilReceiver_f // 判断消息接收者是否为nil
  9. ldr r9, [r0] // r9 = self->isa
  10. CacheLookup NORMAL // 到缓存中查找方法
  11. LCacheMiss: // 方法未缓存
  12. MESSENGER_END_SLOW
  13. ldr r9, [r0, #ISA]
  14. b __objc_msgSend_uncached
  15. LNilReceiver: // 消息接收者为nil处理
  16. mov r1, #0
  17. mov r2, #0
  18. mov r3, #0
  19. FP_RETURN_ZERO
  20. MESSENGER_END_NIL
  21. bx lr
  22. LMsgSendExit:
  23. END_ENTRY objc_msgSend

一个方法的调度主要包括以下几个步骤:

  • 判断接收者是否为nil,如果为nil,清空寄存器,消息发送返回nil
  • 到类缓存中查找方法,如果存在直接返回方法

没有找到缓存,到类的方法列表中依次寻找

  1. IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls)
  2. {
  3. return lookUpImpOrForward(cls, sel, obj,
  4. YES/*initialize*/, NO/*cache*/, YES/*resolver*/);
  5. }
  6. IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
  7. bool initialize, bool cache, bool resolver)
  8. {
  9. IMP imp = nil;
  10. bool triedResolver = NO;
  11. runtimeLock.assertUnlocked();
  12. // 优先查找缓存
  13. if (cache) {
  14. imp = cache_getImp(cls, sel);
  15. if (imp) return imp;
  16. }
  17. //runtimeLock在方法搜索期间保持,使方法查找+缓存填充原子相对于方法添加。否则添加一个类别,但是会无限期地忽略它,因为在代表类别的缓存刷新之后,缓存会用旧值重新填充
  18. runtimeLock.lock();
  19. checkIsKnownClass(cls);
  20. if (!cls->isRealized()) {
  21. cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
  22. // runtimeLock may have been dropped but is now locked again
  23. }
  24. // 如果类未初始化,对其进行初始化。如果这个消息是initialize,那么直接进行类的初始化
  25. if (initialize && !cls->isInitialized()) {
  26. cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
  27. }
  28. retry:
  29. runtimeLock.assertLocked();
  30. // Try this class's cache.
  31. // 遍历缓存方法,如果找到,直接返回
  32. imp = cache_getImp(cls, sel);
  33. if (imp) goto done;
  34. // 从当前类的方法列表中查找
  35. {
  36. Method meth = getMethodNoSuper_nolock(cls, sel);
  37. if (meth) {
  38. log_and_fill_cache(cls, meth->imp, sel, inst, cls);
  39. imp = meth->imp;
  40. goto done;
  41. }
  42. }
  43. // 沿着继承c链从父类的缓存查找,如果没查找到,从父类的方法列表中查找
  44. {
  45. unsigned attempts = unreasonableClassCount();
  46. for (Class curClass = cls->superclass;
  47. curClass != nil;
  48. curClass = curClass->superclass)
  49. {
  50. // Halt if there is a cycle in the superclass chain.
  51. if (--attempts == 0) {
  52. _objc_fatal("Memory corruption in class list.");
  53. }
  54. // 父类缓存
  55. imp = cache_getImp(curClass, sel);
  56. if (imp) {
  57. if (imp != (IMP)_objc_msgForward_impcache) {
  58. //如果在父类方法中找到,,在当前类缓存列表中添加
  59. log_and_fill_cache(cls, imp, sel, inst, curClass);
  60. goto done;
  61. }
  62. else {
  63. // Found a forward:: entry in a superclass.
  64. // Stop searching, but don't cache yet; call method
  65. // resolver for this class first.
  66. break;
  67. }
  68. }
  69. // 父类方法列表查找.
  70. Method meth = getMethodNoSuper_nolock(curClass, sel);
  71. if (meth) {
  72. log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
  73. imp = meth->imp;
  74. goto done;
  75. }
  76. }
  77. }
  78. // 没有找到任何的方法实现,进入消息转发第一阶段“动态方法解析”
  79. // 调用+ (BOOL)resolveInstanceMethod: (SEL)selector
  80. // 征询接收者所属的类是否能够动态的添加这个未实现的方法来解决问题
  81. if (resolver && !triedResolver) {
  82. runtimeLock.unlock();
  83. resolveMethod(cls, sel, inst);
  84. runtimeLock.lock();
  85. // Don't cache the result; we don't hold the lock so it may have
  86. // changed already. Re-do the search from scratch instead.
  87. triedResolver = YES;
  88. goto retry;
  89. }
  90. // 仍然没有找到方法实现进入消息转发第二阶段
  91. //先后会调用 -(id)forwardingTargetForSelector: (SEL)selector
  92. // 以及 - (void)forwardInvocation: (NSInvocation*)invocation 进行最后的补救
  93. imp = (IMP)_objc_msgForward_impcache;
  94. cache_fill(cls, sel, imp, inst);
  95. done:
  96. runtimeLock.unlock();
  97. return imp;
  98. }
  • 以上为方法调用的全部过程,主要分为以下步骤:
  • 查找是否存在对应的方法缓存,如果存在直接返回调用。为了优化性能,方法的缓存使用了散列表的方式
  • 未找到缓存,到类本身或顺着类结构向上查找方法实现。如果在这个步骤中找到了方法的实现,那么将它加入到方法缓存中以便下次调用能快速找到。如果在类自身中没有找到方法实现,那么循环获取父类,重复上面的查找动作,找到后再将方法缓存到本类而非父类的缓存中
  • 未找到任何方法实现,触发消息转发机制进行最后补救
  • 其中消息转发分为两个阶段,第一个阶段我们可以通过动态添加方法之后让编译器再次执行查找方法实现的过程;第二个阶段称作备援的接收者,就是找到一个接盘侠来处理这个事件
  • 消息转发

  1. //本例中run为实例发放,walk为类方法
  2. + (BOOL)resolveInstanceMethod:(SEL)sel{
  3. NSLog(@"%@",NSStringFromSelector(sel));
  4. if(sel == @selector(run)){
  5. class_addMethod([self class], sel, (IMP)class_getMethodImplementation([self class], @selector(testRun)), nil);
  6. return YES;
  7. }
  8. return [super resolveClassMethod:sel];
  9. }
  10. + (BOOL)resolveClassMethod:(SEL)sel{
  11. NSLog(@"%@",NSStringFromSelector(sel));
  12. if(sel == @selector(walk)){
  13. bool success = class_addMethod(objc_getMetaClass([NSStringFromClass(self) UTF8String]), sel, (IMP)class_getMethodImplementation(objc_getMetaClass([NSStringFromClass(self) UTF8String]), @selector(testWalk)), "v@:");
  14. // //"v@:“,按顺序分别表示:
  15. // // v : v表示返回值为void
  16. // // @ :参数id(self)
  17. // // : :SEL(_cmd)对象
  18. return YES;
  19. }
  20. return [super resolveClassMethod:sel];
  21. }
  22. //如果没有做上面两个添加方法操作,可以让其他对象来处理这个消息
  23. - (id)forwardingTargetForSelector:(SEL)aSelector{
  24. if([NSStringFromSelector(aSelector) isEqualToString:@"walk"]){
  25. // return [[Tools alloc]init]; //实例方法返回
  26. return [Tools class]; //类方法返回
  27. }
  28. return [super forwardingTargetForSelector:aSelector];
  29. }
  30. //methodSignatureForSelector需要和forwardInvocationf同时实现
  31. //methodSignatureForSelector:和forwardInvocation:。methodSignatureForSelector:的作用在于为另一个类实现的消息创建一个有效的方法签名,必须实现,并且返回不为空的methodSignature,否则会crash
  32. //forwardInvocation:将选择器转发给一个真正实现了该消息的对象。
  33. - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
  34. NSString *selStr = NSStringFromSelector(aSelector);
  35. if ([selStr isEqualToString:@"run"]){
  36. // return [NSMethodSignature signatureWithObjCTypes:nil];
  37. return [NSMethodSignature signatureWithObjCTypes:"v@:"];
  38. // return [NSMethodSignature methodSignatureForSelector:(IMP)class_getMethodImplementation([self class], @selector(testRun))];
  39. }
  40. return [super methodSignatureForSelector:aSelector];
  41. }
  42. - (void)forwardInvocation:(NSInvocation *)anInvocation{
  43. //重新定义消息接收者 改变id
  44. // [anInvocation invokeWithTarget:[[Tools alloc] init]];
  45. //改变消息的sel
  46. anInvocation.selector = @selector(testRun);
  47. [anInvocation invokeWithTarget:self];
  48. }

缓存查找

  1. bucket_t * cache_t::find(SEL s, id receiver)
  2. {
  3. assert(s != 0);
  4. bucket_t *b = buckets();
  5. mask_t m = mask();
  6. //cache_hash ( return (mask_t)(uintptr_t)sel & mask; //获取存储在散列表中的hash下标)
  7. mask_t begin = cache_hash(s, m);
  8. mask_t i = begin;
  9. do {
  10. //如果找到则返回
  11. if (b[i].sel() == 0 || b[i].sel() == s) {
  12. return &b[i];
  13. }
  14. //hash存在冲突则继续向下查找
  15. } while ((i = cache_next(i, m)) != begin);
  16. // hack
  17. Class cls = (Class)((uintptr_t)this - offsetof(objc_class, cache));
  18. cache_t::bad_cache(receiver, (SEL)s, cls);
  19. }

当前类中的方法查找:

对于已排序好的列表,采用二分查找算法查找

没有排序的列表,采用遍历的方法查找

  1. static method_t *search_method_list(const method_list_t *mlist, SEL sel)
  2. {
  3. int methodListIsFixedUp = mlist->isFixedUp();
  4. int methodListHasExpectedSize = mlist->entsize() == sizeof(method_t);
  5. if (__builtin_expect(methodListIsFixedUp && methodListHasExpectedSize, 1)) {
  6. //采用二分查找
  7. return findMethodInSortedMethodList(sel, mlist);
  8. } else {
  9. // Linear search of unsorted method list
  10. //线性查找
  11. for (auto& meth : *mlist) {
  12. if (meth.name == sel) return &meth;
  13. }
  14. }
  15. #if DEBUG
  16. // sanity-check negative results
  17. if (mlist->isFixedUp()) {
  18. for (auto& meth : *mlist) {
  19. if (meth.name == sel) {
  20. _objc_fatal("linear search worked when binary search did not");
  21. }
  22. }
  23. }
  24. #endif
  25. return nil;
  26. }

文章部分内容参考:https://www.jianshu.com/p/f9cdaccc9f88


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/1022515?site
推荐阅读
相关标签
  

闽ICP备14008679号