写点什么

3 个 iOS 开发典型问题解答

  • 2019-04-12
  • 本文字数:2643 字

    阅读完需:约 9 分钟

3个 iOS 开发典型问题解答

你好,我是戴铭,专栏上线以来,我收到了很多同学非常用心的反馈,有问题、建议、心得和经验,当然提的问题居多。虽然我未在评论区对每条留言做出回复,但是我对大家提出的问题都一一记录了下来,内容很丰富,我进行了汇总和整理,发布到专栏答疑中,感兴趣的小伙伴可以去看看。


更多热点问题答疑


在这里,我先挑选并展开了 3 个典型问题,希望为你抽丝剥茧,答疑解惑。

动态库加载方式的相关问题

问题:

@五子棋 在看完第 5 篇文章“链接器:符号是怎么绑定到地址上的?”后,关于动态库是否参与链接的问题,通过私信和我反馈了他的观点。


他指出:动态库也是要参与链接的,不然就没法知道函数的标记在哪儿。

解答:

为了帮助大家理解这个问题,我把与之相关的内容,再和你展开一下。


我在第 5 篇文章中,是这么阐述这部分内容的:


Mach-O 文件是编译后的产物,而动态库在运行时才会被链接,并没参与 Mach-O 文件的编译和链接,所以 Mach-O 文件中并没有包含动态库里的符号定义。也就是说,这些符号会显示为“未定义”,但它们的名字和对应的库的路径会被记录下来。运行时通过 dlopen 和 dlsym 导入动态库时,先根据记录的库路径找到对应的库,再通过记录的名字符号找到绑定的地址。


细细想来,关于这个问题,更严谨的说法应该是,加载动态库的方式有两种:


  • 一种是,在程序开始运行时通过 dyld 动态加载。通过 dyld 加载的动态库需要在编译时进行链接,链接时会做标记,绑定的地址在加载后再决定。

  • 第二种是,显式运行时链接(Explicit Runtime Linking),即在运行时通过动态链接器提供的 API dlopen 和 dlsym 来加载。这种方式,在编译时是不需要参与链接的。

  • 不过,通过这种运行时加载动态库的 App,苹果公司是不允许上线 App Store 的,所以只能用于线下调试环节。关于这种方式的适用场景,我在专栏第 6 篇“App 如何通过注入动态库的方式实现极速编译调试?”中有举例说明过。


在第 5 篇文章中,我将动态库的这两种加载方式混在一起说了,让你感到些许困惑,所以在这里我特地做个补充说明。

App 启动速度的相关问题

专栏的第 2 篇文章“App 启动速度怎么做优化与监控?”中的大部分问题,我都直接在评论区回复了。今天主要和大家聊一下课后作业的实现问题。

问题:

按照今天文中提到的 Time Profiler 工具检查方法耗时的原理,你来动手实现一个方法耗时检查工具吧。


虽然这个问题的思路,我在文章中提到了,但还是有很多同学感觉无从下手。接下来,我们就再一起来看看这个思考题。

解答:

关于实现思路,我在文章中写到:


定时抓取主线程上的方法调用堆栈,计算一段时间里各个方法的耗时。


我们再一起看一下这个实现思路(我原本未在文中详细展开,是希望多留点思考空间给你)。动手写耗时检查工具时,首先需要开启一个定时器,来定时获取方法调用堆栈。一段时间内方法调用堆栈相同,那么这段时间,就是这个方法调用堆栈的栈顶方法耗时。


这个解题思路里很关键的一步,也是你最容易忽视的一步,就是应该怎么做好获取方法调用堆栈。


callstackSymbols 是一种获取方法调用栈的方法,但是只能获取当前线程的调用栈,为了把对主线程的影响降到最小,获取当前线程调用栈的工作就需要在其他线程去做。


所以,这个解题思路就需要换成:使用系统提供的 task_threads 去获取所有线程,使用 thread_info 得到各个线程的详细信息,使用 thread_get_state 方法去获取线程栈里的所有栈指针。


如果接下来立刻进行符号化去获取方法名,那么就需要去 __LINKEDIT segment 里查找栈指针地址所对应符号表的符号,特别当你设置的时间隔较小的时候,符号化过程会持续消耗较多的 CPU 资源,从而影响主线程。


所以,获取到栈指针后,我们可以不用立刻做符号化,而是先使用一个结构体将栈地址记录下来,最后再统一符号化,将对主线程的影响降到最低,这样获取的数据也会更加准确。


我们可以把记录栈地址的结构体设计为通用回溯结构,代码如下:


typedef struct SMStackFrame {    const struct SMStackFrame *const previous;    const uintptr_t return_address;} SMStackFrame;
复制代码


在这段代码中, previous 记录的是上一个栈指针的地址。考虑 CPU 性能,记录堆栈的数量也不必很多,取最近几条即可。通过栈基地址指针获取当前栈指针地址的关键代码如下:


// 栈地址初始化SMStackFrame stackFrame = {0};// 栈基地址指针const uintptr_t framePointer = smMachStackBasePointerByCPU(&machineContext);if (framePointer == 0 || smMemCopySafely((void *)framePointer, &stackFrame, sizeof(stackFrame)) != KERN_SUCCESS) {    return @"Fail frame pointer";}// 下面的8表示堆栈数量for (; i < 8; i++) {  // 记录栈地址    buffer[i] = stackFrame.return_address;    if (buffer[i] == 0 || stackFrame.previous == 0 || smMemCopySafely(stackFrame.previous, &stackFrame, sizeof(stackFrame)) != KERN_SUCCESS) {        break;    }}
复制代码

关于 Clang 的相关问题

专栏已经更新的第 7~第 10 这 4 篇文章中,都涉及到了 Clang 的知识以及应用。


在第 7 篇“Clang、Infer 和 OCLint ,我们应该使用谁来做静态分析?”中,介绍的 3 款静态分析工具都用到了 Clang,而且 Clang 本身也提供了 LibTooling 这种强大的 C++ 接口来方便定制独立的工具。

问题:

Clang 的知识需要投入大量精力才能掌握好,有同学可能会有疑问:”我掌握这些偏底层的知识有什么用呢,好像也解决不了我在现实开发工作中遇到的问题啊?“

解答:

在我看来,你只有掌握了某个方面的知识,在工作中碰到问题时,才能够想到用这个知识去解决问题。如果你都不知道有这么一种方法,又怎么会用它去解决自己的问题呢?


就比如说,你掌握了 Clang 的知识,那在研究无侵入的埋点方案应该如何实现时,你才能可能会想到用 Clang 的 LibTooling 来开发一个独立的工具,专门以静态方式插入埋点的代码;只有掌握了 Clang 的知识,当你在面对代码量达到百万行的 App 包瘦身需求时,才会想到通过 Clang 静态分析来开发工具,去检查无用的方法和类。


当你掌握了 Clang 的相关知识后,编译前端的技术也就掌握得差不多了;在理解了编译前端的词法分析和语法分析的套路后,脱离 Clang 的接口完成第 8 篇文章“如何利用 Clang 为 App 提质?”的课后作业,也就没什么难度了。


篇幅有限,关于 3 个 iOS 开发典型问题的答疑就先到这里。我希望通过这三个问题,可以帮你搞明白那些让你困惑的知识点,逐步地建立起自己的知识体系。


出处:极客时间《iOS开发高手课》专栏


点击这里试看或订阅《iOS开发高手课》


2019-04-12 14:302365

评论

发布
暂无评论
发现更多内容

web前端培训班学习前景怎么样

小谷哥

AR空间音频能力,打造沉浸式声音体验

HMS Core

华为 AR HMS Core

JavaScript刷LeetCode拿offer-位运算

Geek_07a724

JavaScript LeetCode

JavaScript刷LeetCode拿offer-高频链表题

Geek_07a724

JavaScript LeetCode

说说Nodejs高并发的原理

coder2028

node.js

JS知识点梳理之作用域、作用域链、柯里化、闭包

hellocoder2029

JavaScript

别再自己瞎写工具类了,Spring Boot 内置工具类应有尽有, 建议收藏!!

程序知音

Java spring springboot java面试 后端技术

说说Nodejs高并发的原理

coder2028

node.js

vivo鲁京辉:从体验到生态,守正创新,vivo隐私安全再升级

Geek_2d6073

转行数据分析?你可能需要这块敲门砖!

博文视点Broadview

大数据培训的前途怎么样

小谷哥

刷完这19道leetcode二分查找算法,不信进不了大厂

Geek_07a724

JavaScript LeetCode

🍃【Spring专题】「开发指南」手把手教你将@Schedule任务调度升级为分布式调度@DistributeSchedule

洛神灬殇

spring 分布式任务调度 任务调度 scheduler 11月月更

自学前端没有找到工作,怎么做呢

小谷哥

IM通讯协议专题学习(一):Protobuf从入门到精通,一篇就够!

JackJiang

网络编程 即时通讯 IM

赛况激烈!2022 OceanBase数据库大赛50强诞生

OceanBase 数据库

推荐一份关于JDK实现&源码解读的经典著作,强烈建议Java程序员都看看

程序员小毕

程序员 程序人生 Java并发 后端 jdk源码

相约2023,高通公司宣布参加第六届进博会

科技热闻

深入理解Metrics(三):Histograms

冰心的小屋

Java metrics Histograms

java培训学习机构怎么选择

小谷哥

大数据培训和自学哪个好

小谷哥

【收藏】锂电材料工厂设备日常保养评分标准

PreMaint

设备管理 设备日常保养

细说js变量、作用域和垃圾回收

hellocoder2029

JavaScript

河北省等保测评机构新名单-行云管家

行云管家

网络安全 堡垒机 等级保护 等保测评 等级测评

前端leetcde算法面试套路之树

js2030code

JavaScript LeetCode

全网首次公开!设计模式+代码+JVM调优,不愧是阿里Java性能优化核心原理全解手册

Java全栈架构师

程序员 程序人生 性能优化 后端 java面试

声网首席科学家钟声:感知实时互联网

声网

人工智能 模型

什么是双机热备?实现方式有哪些?

行云管家

高可用 ha 热备 双机热备

【C语言】double 关键字

謓泽

11月月更

Node.js实现大文件断点续传

coder2028

node.js

JS词法环境和执行上下文

hellocoder2029

JavaScript

  • 扫码加入 InfoQ 开发者交流群
3个 iOS 开发典型问题解答_语言 & 开发_戴铭_InfoQ精选文章