您的位置:  首页 > 技术杂谈 > 正文

【深度讲解】iOS应用性能数据采集原理和优化实践 | 内附代码&案例

2022-03-09 19:00 https://my.oschina.net/yunzhihui/blog/5479613 云智慧AIOps社区 次阅读 条评论

作者简介

刘徐兵(Alvin Liu),云智慧/开发经理。曾在高德、当当有多年大型App开发经验,在云智慧从事APM SDK研发工作5+年。对App开发和性能优化有深入的研究和实践。

iOS应用数据采集的基础 Objective-C Runtime

1、消息转发

Objective-C语言扩展了C语言,扩展的核心在于引入了Runtime库,使Objective-C语言拥有了面向对象和动态运行时的特性。而动态运行时机制的核心和表现是消息转发机制。

Objective-C语言拥有动态运行时的机制,方法的执行是在运行阶段决定的而不是在编译阶段决定的。而方法执行的实质是向对象发送了一个消息,官方API为

objc_msgSend(void /* id self, SEL op, ... */ )

objc_msgSend有2个常用参数:id self 和 SEL op,用于标识对象和方法,即向某个对象发送了某个消息。因此Objective-C的[instance method]调用会被编译器转换成C语言API objc_msgSend的调用。

以下消息转发机制原理图从官方文档翻译而来:

消息转发机制示例图

如上图所示,Objective-C的消息转发流程中,在当前对象方法列表中找不到方法的实现时,运行时环境会依次进行三个阶段的查找

第一阶段

对象在收到无法处理的消息时,首先调用所属类的下列类方法。

+(BOOL)resolveInstanceMethod:(SEL)sel{ 

    //默认返回NO 

    return NO;

} 

+(BOOL)resolveClassMethod:(SEL)sel{ 

    //默认返回NO 

    return NO;

}

如果在其中找到了方法的实现,则进行消息处理;否则就进入第二阶段。

第二阶段

在当前类里找不到该方法的实现时,运行时系统尝试更换调用的对象,会调用如下方法

-(id)forwardingTargetForSelector:(SEL)aSelector{}

如果在该方法中还找不到方法的实现,就进入第三阶段。

第三阶段

到这里是最后一个阶段,通过创建NSInvocation实例,将与未处理的消息有关的细节封装起来,运行时系统调用的接口为

-(void)forwardInvocation:(NSInvocation *)anInvocation{}

该方法会沿着类的继承链一直往上调用,直至在NSObject的该方法中抛出doesNotRecognizeSelector:异常。

2、函数指针

SEL是Objective-C语言的方法选择器,也就是selector的指针。再往底层去,每个方法SEL还对应着一个IMP函数指针,指向方法实现的首地址。官方API为:

typedef id (*IMP)(id, SEL, ...); 

有了它就可以直接执行IMP指向的函数(方法)了。

以上介绍了理论基础,下面介绍下基于Objective-C语言动态运行时的方法拦截(Hook)操作。

Hook原理

Hook步骤

  1. 使用Category特性往类中添加用于拦截的方法SEL
  2. 使用系统runtime的接口(Swizzle)交换方法实现体(IMP)

Hook使用示例图

如图所示,开发者调用原有方法时,会转发到拦截的方法里,因交换了原方法与拦截方法的入口地址(IMP),在拦截方法执行结束时能调回原方法,对原有业务没有影响。

Hook回调函数

难点:不是类的实例方式,不能用Category特性。

优化前: 全工程扫描,再拦截。

缺点:只能在主线程中操作,扫描文件数和消耗的时间与App的规模大小成正比。

回调函数拦截优化

步骤:

  1. 针对要使用protocol的系统类使用Category特性添加拦截方法
  2. 拦截设置delegate的setter方法,获取到delegate的实例
  3. 针对获取到的delegate实例再进行回调方法拦截

优化后

1、延迟拦截:在方法被调用时才被拦截而且只拦截一次

2、SDK的启动操作跟App的业务和规模无关,对App的影响降到最低

崩溃解码实践

目的: 了解iOS崩溃解码原理

崩溃解码步骤

  1. 虚拟内存偏移量:145624->0x238d8->0x00000001000238d8
  2. 使用dwarfdump命令,将dSYM文件解析成可读文件

dwarfdum-e--debug-info 包含全部类和方法的代码和地址映射信息->信息文件

dwarfdum-e--debug-line 包含全部类的代码行号和地址映射信息->行号文件

  1. 在信息文件中查找偏移量所在的类和方法,得到崩溃的类和对应的方法
  2. 在行号文件中查找偏移量所在的行号,得到具体的崩溃行号

虚拟内存偏移量:

0x00000001000238d8

dSYM文件解析命令

  1. symbolicatecrash->全文本解析
  2. atos(mac)/atosl(linux)->单行解析

H5页面监控原理

以上介绍了系统原生接口的数据采集原理,下面介绍下H5页面数据采集实现原理。

H5页面数据采集通过自动往H5页面注入JS代码实现。流程如下图所示

H5页面注入JS代码示意图

如上图所示,在UIWebView时代,通过NSURLProtocol协议簇的接口,拦截到加载页面数据的接口,获取到H5数据块,检测数据块中的<head>标签,将JS代码注入到<head>中。这样JS代码就和H5页面代码一起加载、工作,能够采集到H5页面的相关信息。JS代码采集到信息后,通过iframe的方式触发UIWebView的回调将信息发送给原生SDK端存储、上报。

在WKWebView时代,由于WKWebView是单独的进程,在App里从系统的网络协议簇无法获取到WKWebView的数据,可通过拦截WKWebView的回调函数去执行JS代码,能起到与UIWebView同样的数据采集效果。这里不再赘述。

PS:JS代码的工作原理是另一个技术领域的范畴,不在这里赘述。

写在最后

近年来,在AIOps领域快速发展的背景下,IT工具、平台能力、解决方案、AI场景及可用数据集的迫切需求在各行业迸发。基于此,云智慧在2021年8月发布了AIOps社区, 旨在树起一面开源旗帜,为各行业客户、用户、研究者和开发者们构建活跃的用户及开发者社区,共同贡献及解决行业难题、促进该领域技术发展。

社区先后 开源 了数据可视化编排平台-FlyFish、运维管理平台 OMP 、云服务管理平台-摩尔平台、 Hours 算法等产品。

项目介绍:https://www.cloudwise.ai/flyFish.html

Github地址: https://github.com/CloudWise-OpenSource/FlyFish

Gitee地址: https://gitee.com/CloudWise/fly-fish

请您通过下方链接了解我们,添加小助手微信(xiaoyuerwie),备注:飞鱼。申请加入开发者交流群,可与业内大咖进行1V1交流!

也可联系我们了解云智慧AIOps资讯,了解云智慧开源社区其他项目开源情况!
展开阅读全文
  • 0
    感动
  • 0
    路过
  • 0
    高兴
  • 0
    难过
  • 0
    搞笑
  • 0
    无聊
  • 0
    愤怒
  • 0
    同情
热度排行
友情链接