iOS 探究系列相关文章 :
iOS 探究 — alloc、init 与 new 的剖析
iOS 探究 — 内存对齐原理剖析
iOS 探究 — isa 的初始化和指向剖析
iOS 探究 — 类的结构剖析(一)
iOS 探究 — 类的结构剖析(二)
iOS 探究 — 音讯查找流程(一)
iOS 探究 —ios系统 音讯查找流程(二)
iOS 探究 — 动态办法抉择剖析
iOS 探究 — 音讯转发初始化流程剖析
iOS 探究 — 离屏渲染
iOS 探究 — KVC 原理剖析
经过前面的研究知道了, 办法的过程实际上便是 objc_msgSend
进行音讯查找的过程。在进行音讯查找的过程中, 假设没有找到对应的办法完结的话体系会做出一些处理。处理分为两部分, 首先是做了一工龄差一年工资差多少次 动态办法抉择
之后进行 retry
重新查找, 接下来来看一下这部分都做了什么:
动态办法抉择
1. _class_resolveMethod
动态办法抉择的部分代码:
// 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;
}
经过履行 _class_resolios15veMeios越狱thod
办法进行办法抉择, 然后在最终经过 goto retry
重新进入音讯查找流程, 下源码交易平台面看看这源码之家个办法的内部做了什么:
//
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);
}
}
}
在该办法内部进行了一个判别:
- 假如
clsiOS
不是元类目标的话调用_class_resolveInstanceMethod
。 - 假如传进来的
cls
是元类目标的话就去调用_class_resolveClassMethod
, 调用完以后再去lookUpIm枸杞pOrNil 查找一遍办法, 而且这儿传的 resolver = NO 所以不会再次进入抉择
, 假如仍然没有找到就去走一遍目标办法的抉择
2. _class_re初始化磁盘soios系统lveClassMethod 和 _class_resolveInstanceMethod
// _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));
}
}
}
// _class_resolveClassMethod
static void _class_resolveClassMethod(Class cls, SEL sel, id inst)
{
assert(cls->isMetaClass());
if (! lookUpImpOrNil(cls, SEL_resolveClassMethod, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/))
{
// Resolver not implemented.
return;
}
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
// _class_getNonMetaClass 办法
// 这儿的 cls 应该是元类目标, 经过办法取得对应的类目标然后发送音讯
bool resolved = msg(_class_getNonMetaClass(cls, inst),
SEL_resolveClassMethod, sel);
// Cache the result (good or bad) so the resolver doesn't fire next time.
// +resolveClassMethod adds to self->ISA() 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 resolveClassMethod:%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));
}
}
}
这两源码中的图片个办法的内部完结基本上都是相同的, 仅有不同的当地在于下面源码编辑器这一行:
//
bool resolved = msg(cls, SEL_resolveInstanceMethod, sel);
//
bool resolved = msg(_class_getNonMetaClass(cls, inst),
SEL_resolveClassMethod, sel);
由于 目标办法 和 类办法 的原因在这儿进行了分开处理, 这两个流程简直都源码编辑器是相同的, 所以接下来主要看 目标办法的流程, 有爱好的能够到源码中再看看类办法的流程。
能够看到这儿是给传进来的类目标发送了一个音讯, 调用的办法是 SEL_resolveInstanceMethod
, 还有一个参数便是之前一直在查找的办法编号 sel
。接下来去大局搜索看看能不能找到相关的办法
3. resolveInstanceMethod
直接复制 SEL_resolveInstanceMethod
得到的是一个下面的东西:
SEL SEL_resolveInstanceMethod = NULL;
然后ios下载测验把前面的 SEL_
去掉, 果然在 NSObject
类中找到了源码网站相源码时代关内容:
// NSObject.h
+ (BOOL)resolveClassMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
+ (BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
// NSObject.m
+ (BOOL)resolveClassMethod:(SEL)sel {
return NO;
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
return NO;
}
可是这两个办法都只是简略的返回了一个 NO, 并google没有做任何处理, 为什么呢?源码编辑器
我觉得这应该是体系给咱们初始化失败是怎么解决提供的一种容错手段吧, 咱们能够经过重写这两个办法。拿到传递过来的 SEL, 然后动态的给这个 SEL 增Go加一个办法完结, 这样就能够有用的防止程ios14.4.1更新了什么序溃散。
所以之所以说是
动态办法源码编程器抉择
, 要害点就应该在这儿。
4. 动态ios下载办法抉择事例
预源码之家备代码:
// Student
@interface Student : NSObject
- (void)sayHello;
+ (void)sayObjc;
@end
@implementation Student
@end
// main.m
Student *student = [[Student alloc] init];
[student sayHello];
1. 实例办法
办法抉择完结:
+ (BOOL)resolveInstanceMethod:(SEL)sel {
// sayHello 能够是自己定义的任何符号判别
// 这儿还能够做一步过错日志上传等操作
if (sel == @selector(sayHello)) {
NSLog(@"办法未找到: %@---%@", self, NSStringFromSelector(sel));
IMP instanceIMP = class_getMethodImplementation(self, @selector(insteadMethod));
Method instanceMethod = class_getInstanceMethod(self, @selector(insteadMethod));
const char * types = method_getTypeEncoding(instanceMethod);
return class_addMethod(self, sel, instanceIMP, types);
}
return NO;
}
- (void)insteadMethod{
NSLog(@"我是代替办法");
}
如上所示, 经过在 resolveInstanceMethod
办法里边给传过来的 sel
动态增加了一个办法完结, 就有用的防止了溃散ios是什么意思。而且在这儿还能够做一些其他比方过错日志收集等的工作。
2. 类办法
由于类办法是存储在元类中的, 所以在进行类办法动态抉择的时候是给元类增加一个办法完结 , 代码如下:
+ (BOOL)resolveClassMethod:(SEL)sel {
if (sel == @selector(sayObjc)) {
NSLog(@"办法未找到: %@---%@", self, NSStringFromSelector(sel));
IMP classIMP = class_getMethodImplementation(objc_getMetaClass("LGStudent"), @selector(insteadClassMethod));
// 类办法存储在元类中, 而且是以实例办法的方式存储
Method classMethod = class_getInstanceMethod(objc_getMetaClass("LGStudent"), @selector(insteadClassMethod));
const char * types = method_getTypeEncoding(classMethod);
return class_addMethod(objc_getMetaClass("LGStudent"), sel, classIMP, types);
}
return NO;
}
+ (void)insteadClassMethod {
NSLog(@"我是代替类办法");
}
3. 类办法的 resolveInstanceMethod
由于 类办法
是以目标办法的方式存贮到元类中的, 可是元类和根元类是体系生成的咱们无法改变。联想到 isa 的流程图, 在根元类之工商银行后会最终指向 NSObject 类, 所以在 NSObje源码交易平台ct 里边去完结 resolveInstios模拟器anceMethod
应该能够完结类办法的动态解析:
// NSObject+Test.m
+ (BOOL)resolveInstanceMethod:(SEL)sel {
// sayHello 能够是自己定义的任何符号判别
// 这儿还能够做一步过错日志上传等操作
if (sel == @selector(sayHello)) {
NSLog(@"办法未找到: %@---%@", self, NSStringFromSelector(sel));
IMP instanceIMP = class_getMethodImplementation(self, @selector(insteadMethod));
Method instanceMethod = class_getInstanceMethod(self, @selector(insteadMethod));
const char * types = method_getTypeEncoding(instanceMethod);
return class_addMethod(self, sel, instanceIMP, types);
}
return NO;
}
- (void)insteadMethod{
NSLog(@"我是代替办法");
}
最终结果是可行的, 对于在 resolverClassMethod
失败只要, 假如初始化电脑时出现问题未进行更改在 NSObject
里边有对应的目标办法抉择相同可行。
5. 总结
- 在进行音讯查找流程中假如没有找到相关完结就会ios模拟器经过Go调用
_class_resolveMetho初始化电脑的后果d
办法进入动态办法抉择流程 - 在这个办法里边判别ios下载当前的类目标是元类仍源码之家是普通的类
- 假如不是元类, 说明查找的是
目标办法
, 进入_class_resolveInstanceMethod
流程, 经过objc_msgSen初始化磁盘d
发送音讯去调用resolveInstanceMethod
办法。咱们能够经过重写这个办法, 在调用这个办法时拿到传过来的办法SEL
, 动态的给这个SEL
增加一个办法完结, 这样就能够防止溃散 - 假如是元类ios越狱, 说明查找的是
类办法
, 会首先进入_class_resolveClassMet公司让员工下班发手机电量截图hod
流程 (流程大致跟目标办法的ios下载差不多) - 上面流程结束后, 会调用一次
lookUpImpOrNil
再对当前音讯进行一次查找, 由于在抉源码1688择的过程中或许会把办法增加进去 - 调用
lookUpImpOrNil
假如仍然没有, 就ios16进入到_c宫颈癌lass_resolveInstance
流程, 由于NSObject
的特殊性, 假如在 NSObject 里边有对当前类办法完结的目标办法抉择也能够处理问题
已然 NSO初始化是什么意思bject 里的目标办法抉择能够处理不管是目标办法仍是类办法悉数得问题, 那么是不是能够加一个 NSObject 分类, 然后在这儿工资超过5000怎么扣税面处理一切的动态办法抉择问ios应用商店题呢?
能够是能够, 可初始化失败是怎么解决是并不是特别好。
- 把一切的抉择都放在这儿的话耦初始化电脑的后果合度会变得十分高
- 把项目里的种种办法抉择都集中到一同, 逻辑判别也会十分十分多
- 有或初始化电脑时出现问题未进行更改许运用的一些三方库中他人也做了这种处理, 这样就容易发生冲突