我们先从lookUpImpOrForward看起
- realizeAndInitializeIfNeeded_locked jump
- initializeAndLeaveLocked jump
- initializeAndMaybeRelock jump
- initializeNonMetaClass jump
![]()
- callInitialize jump
callInitialize 会把当前的class的initialize进行系统调用
二分法查找
举例现在1-8共8个方法,count=8,假设现在正确的方法在7号位置,当前0号位置开始,开始右移
base + (count >> 1) = 0 + (8 >> 1) = 1000 >> 1 = 0100 = 4 //当前位置
base = probe + 1 = 4 + 1 = 5;//当前位置
base = probe + 1 = 5 + 1 = 6;//当前位置
base = probe + 1 = 5 + 1 = 7;//当前位置
count -- = 8 - 1 = 7;
count = 7 >> 1 = 0111 >> 1 = 0011 = 3
count = 6 >> 1 = 0011 >> 1 = 0001 = 1
count -- = 3 - 1 = 2;
count = 2 >> 1 = 0010 >> 1 = 0001 = 1
1 >> 1 = 0001 >> 1 = 0
- 查找流程:自己的类->找父类->缓存查找->lookupimp->父类的缓存(cache)
坑点
1、对象调用一个类方法,是可以成功的,只要在NSObject里处理,在分类添加,根本原因是isa的走位图。
不写m文件里的实现,会报错如下
- _objc_msgForward_impcache
- __objc_forward_handler
//重点代码
_objc_fatal("%c[%s %s]: unrecognized selector sent to instance %p "
"(no message forward handler is installed)",
class_isMetaClass(object_getClass(self)) ? '+' : '-',
object_getClassName(self), sel_getName(sel), self);
动态方法决议
// 单例方法只会执行一次
//3 & 2 = 0011 & 0010 = 0010 = 2
//2 & 2 = 0 = behavior
if (slowpath(behavior & LOOKUP_RESOLVER)) {
behavior ^= LOOKUP_RESOLVER;
return resolveMethod_locked(inst, sel, cls, behavior);
}
再次动态默认赋值
behavior 来源
当方法没有写实现,缓存查找及慢速的递归流程查找后,还是没找到,会导致崩溃!!!
再给最后一个机会,重新开始查找lookUpImpOrForwardTryCache,返回imp return lookUpImpOrForwardTryCache(inst, sel, cls, behavior);
#if CONFIG_USE_PREOPT_CACHES
if (fastpath(cls->cache.isConstantOptimizedCache(/* strict */true))) {
imp = cache_getImp(cls->cache.preoptFallbackClass(), sel);
}
#endif
if (slowpath(imp == NULL)) {
return lookUpImpOrForward(inst, sel, cls, behavior);
}
再次执行lookUpImpOrForward
满足下面的条件,可以再找一次
//判断是不是元类
if (! cls->isMetaClass()) {
// try [cls resolveInstanceMethod:sel]
resolveInstanceMethod(inst, sel, cls);
}
else {
// try [nonMetaClass resolveClassMethod:sel]
// and [cls resolveInstanceMethod:sel]
resolveClassMethod(inst, sel, cls);
if (!lookUpImpOrNilTryCache(inst, sel, cls)) {
resolveInstanceMethod(inst, sel, cls);
}
}
- resolveInstanceMethod jump
- resolve_sel 系统发送这个消息
执行 IMP imp = lookUpImpOrNilTryCache(inst, sel, cls);
在这里执行了两次TryCache
//只要使用了下面的方法 SEL resolve_sel = @selector(resolveInstanceMethod:), 就不会报错
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(cls, resolve_sel, sel);
.m添加如下代码
在程序报错之前,走了上面定义的方法,输出如下
+ (BOOL)resolveInstanceMethod:(SEL)sel{
if (sel == @selector(sayNB)) {
IMP teacherSayImp = class_getMethodImplementation(self, @selector(teacherSay));
Method method = class_getInstanceMethod(self, @selector(teacherSay));
const char *type = method_getTypeEncoding(method);
return class_addMethod(self,sel,teacherSayImp,type);
}
NSLog(@"resolveInstanceMethod : %@-%@",self,NSStringFromSelector(sel));
return [super resolveInstanceMethod:sel];
}
//替换 输出
-[HLTeacher teacherSay]
Program ended with exit code: 0
系统默认实现,系统会帮你兜底。
类方法的动态决议
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(nonmeta, @selector(resolveClassMethod:), sel);
nonmeta 当前元类
resolveClassMethod : HLTeacher-encodeWithOSLogCoder:options:maxLength:
resolveClassMethod : HLTeacher-sayHappy
+[HLTeacher sayHappy]: unrecognized selector sent to class 0x100008508
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[HLTeacher sayHappy]: unrecognized selector sent to class 0x100008508'
resolveClassMethod : HLTeacher-encodeWithOSLogCoder:options:maxLength:
HLTeacher - +[HLTeacher sayHello]
小结:
- 1、OC代码运行是根据方法名SEL(例如sayHappy),然后去寻找方法的实现(IMP)
- 2、如果实现了分类方法(例如上面的代码),所有的方法找不到,我们都能监听到,当找不到方法实现时发送消息
- 3、非苹果源码模块方法名的判断,苹果源代码的不用Hook,Hook的是我们自己写的代码
- 4、AOP 面向切面编程,面向对象编程(OOP)的一种延,可以使业务逻辑各部分之间的耦合度降低,提高程序的可复用性,提高开发效率(扩展)
- 5、OOP 面向对象编程,对象的分工非常明确,会有冗余代码,再提取出公共的类,会造成强依赖,产生强耦合,所以我们应该尽量无侵入,可以通过动态的方法进行注入(runtime运行时的使用),作为切面切入,切点是要切入的方法(例如sayhappy)
- 6、缺点是判断过多,会产生性能消耗
未完待续......