百度一面总结

5,083 阅读6分钟

1. 二进制重排

1.1 clang 插桩具体如何做的

1.2 c++ 方法 clang 插桩能否取到?

不能 可以取到 c 函数,oc 方法,blockswift 方法 但是可以用 -finstrument-functions 在编译期插桩 “hook”,或者使用并不完美但成本最低的静态扫描方案。

1.3 除了二进制重排,还有哪些启动优化的方法

1.3.1 pre main 阶段

  1. Load dylibs
  • 尽量不使用内嵌(embedded)的 dylib,加载内嵌 dylib 性能开销较大
  • 合并已有的 dylib 和使用静态库(static archives),减少 dylib 的使用个数
  • 懒加载 dylib,但是要注意 dlopen() 可能造成一些问题,且实际上懒加载做的工作会更多
  1. Rebase/Bind

dylib 的加载过程中,系统为了安全考虑,引入了 ASLR(Address Space Layout Randomization)技术和代码签名。由于ASLR的存在,镜像(Image,包括可执行文件、dylibbundle)会在随机的地址上加载,和之前指针指向的地址(preferred_address)会有一个偏差(slide),dyld需要修正这个偏差,来指向正确的地址。

  • 减少 ObjC 类(class)、方法(selector)、分类(category)的数量
  • 减少 C++ 虚函数的数量(创建虚函数表有开销)
  • 使用 swiftStructs (内部有优化,符号数量更少)
  1. Objc setUp
  2. Initializers
  • 少在类的 +load方法里做事情,尽量把这些事情推迟到 +initiailize
  • 减少构造器函数个数,在构造器函数里少做些事情
  • 减少 C++ 静态全局变量的个数

1.3.2 main 阶段

  1. 梳理各个二方/三方库,找到可以延迟加载的库,做延迟加载处理,比如放到首页控制器或 tabBar 控制器的 viewDidAppear 方法里,并且保证只执行一次(按项目结构,放在合适的地方)
  2. 梳理业务逻辑,把可以延迟执行的逻辑,做延迟执行处理。比如检查新版本、注册推送通知等逻辑。
  3. 避免复杂/多余的计算。
  4. 避免在用户看到的第一个界面(首页控制器或注册登录页面)的 viewDidLoadviewWillAppear 做太多事情,这 2 个方法执行完,第一个页面才能显示,部分可以延迟创建的视图应做延迟创建/懒加载处理
  5. 首页控制器或注册登录页面用纯代码方式来构建

1.4 link map数据结构

Link Map File 中文直译为链接映射文件,它是在 Xcode 生成可执行文件的同时生成的链接信息文件,用于描述可执行文件的构造部分,包括了代码段和数据段的分布情况

1.4.1 linkMapfile 的组成

  1. PathPath 是生成可执行文件的路径。
  2. ArchArch 指代架构类型。
  3. Object filesObject Files 列举了可执行文件里所有的 obj 以及 tbd。每一行代表对文件的编号。
  4. Sections: 每个 Section 包含了 AddressSizeSegment 以及 SectionMach-O 文件中的虚拟地址最终会被映射到物理地址上,这些地址会被分为不同的段类型: TEXTDATA 以及 LINKEDIT 等。各个段的含义如下:

TEXT 包含了被执行的代码。这些代码是只读、可执行 DATA 包含了包含了将会被更改的数据,例如全局变量、静态变量等,可读写,但是不可执行 LINKEDIT 包含了加载程序的元数据,比如函数名称和地址,只读。 Segment 又被划分成了不同的 Section,不同的 Section 存储了不同的信息,例如 objc _ methname 为方法的名称

  1. Symbols :Symbols包含的信息有:
  • Address:起始地址
  • Size:所占内存大小,这里使用16进制表示。
  • File:该Name所在的文件编号,也就是Object files部分的中括号的数字。
  1. Dead Stripped Symbols

1.5 具体的优化时间获取?有没有看过实际线上优化多少?

  1. pre-main阶段测量DYLD_PRINT_STATISTICS
  2. main()阶段测量:先在 main() 函数里用变量 StartTime didFinishLaunchingWithOptions 再获取一下当前时间

2. VC生命周期

  1. +load: 程序启动后,在系统的 main 函数调用之前,系统就会加载所有的 load 方法,提前进行一些资源包的配置或者 hook,(可以打断点看看结果,本人亲测过)
  2. +initialize: 当前类或者其子类未被初始化过时会首次调用,若以后当前类或者子类再多次初始化时不会再调用,一般提前为初始化做一些工作
  3. +alloc: 系统为当前类分配内存时调用,在C语言中就是 malloc 这一步
  4. -initWithCoder: 通过 storyBoard 方式实例化的 vc,需要经过反序列化,这个方法会被调用
  5. -initWithNibName:bundle: 通过 xib 文件或者 init 方法实例化的 vc,这个方法都会被调用,其实 init 方法最终都会走该方法
  6. -init: 通过纯代码实例化Vc会调用,其最终会调用 initWithNibName:bundle: 方法
  7. -loadView: 实例化Vc后,可以加载一些系统常规的View
  8. -viewDidLoad: 一般加载自定义的view或者初始化属性,视图加载完毕后会调用
  9. -viewWillAppear: 视图即将出现会调用
  10. -viewWillDisappear: 视图即将消失会调用
  11. -viewWillLayoutSubviews: 视图加载完毕后即将要布局
  12. -viewDidLayoutSubviews: 视图加载完毕后布局也完成了
  13. -didReceiveMemoryWarning: 加载视图时,内存消耗太大,出现内存警告,会调用
  14. -dealloc: 实例化被销毁,进行内存的回收会调用

3. copy / mutableCopy

可变对象复制:

都是深拷贝,但是 copy 返回的对象是不可变的。

对于容器而言,其元素对象始终是指针复制。如果需要元素对象也是对象复制,就需要实现深拷贝。

4. NSUserDefault存储位置

~/Library/Preferences 下,用 plist 文件存储

5. Https如何保证数据安全

是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议

公私钥加密之后是否还有加密

对称加密+非对称加密这两种方式,我们可以用非对称加密的方式来传输对称加密过程中的密钥,之后我们就可以采取对称加密的方式来传输数据

6. git reset

可以让 HEAD 这个指针指向其他的地方 它有三种模式,soft, mixed, hard

6.1 reset soft

保留工作目录,并把重置 HEAD 所带来的新的差异放进暂存区 reset --soft 会在重置 HEADbranch 时,保留工作目录和暂存区中的内容,并把重置 HEAD 所带来的新的差异放进暂存区。

6.2 reset mixed

reset 不加参数(mixed):保留工作目录,并清空暂存区 reset 如果不加参数,那么默认使用 --mixed 参数。它的行为是:保留工作目录,并且清空暂存区。也就是说,工作目录的修改、暂存区的内容以及由 reset 所导致的新的文件差异,都会被放进工作目录。简而言之,就是「把所有差异都混合(mixed)放在工作目录中」。

6.3 reset hard

重置 stage 区和工作目录 reset --hard 会在重置 HEADbranch 的同时,重置 stage 区和工作目录里的内容。当你在 reset 后面加了 --hard 参数时,你的 stage 区和工作目录里的内容会被完全重置为和 HEAD 的新位置相同的内容。换句话说,就是你的没有 commit 的修改会被全部擦掉。

学无止境。