当前位置: 首页 > 面试经验 >

分享字节客户端面经(已OC)

优质
小牛编辑
98浏览
2023-03-28

分享字节客户端面经(已OC)

可能有的内容答得不完善不正确,请大家注意甄别。

一面(9.26 55min)
1.     C++ 和 OC 对象生命周期管理(C++ 堆中的对象由程序员手动管理生命周期,或者使用智能指针辅助管理;OC 采取引用计数思路,使用 MRC 和 ARC 以及自动释放池管理对象的生命周期。MRC 的四个原则:1. 自己创建的自己持有(alloc、new、copy、mutableCopy);2.不是自己创建的也可以持有(retain);3.使用结束后有义务释放(release);4.不是自己持有的不可以释放。ARC 不手动添加 retain 和 release,由编译器编译后在适当位置加入持有和释放操作。自己创建的对象也可以不用自己持有,而交由自动释放池管理,当自动释放池被抽干(drain),池管理的对象也被 release 一次,自动释放池可以帮助处理在某一作用域未结束,但已不再需要的对象对内存占用的激增)
2.     C++ 智能指针(四种智能指针 auto_ptr(已弃用),unique_ptr,shared_ptr,weak_ptr,早期的 auto_ptr 和之后的 unique_ptr 是为了实现对某一堆中对象的唯一管理,但当移交管理的时候 auto_ptr 采取的是拷贝语义,unique_ptr 采用的是移动语义,使得后者通用性更好;shared_ptr 采取引用计数思路管理对象生命周期,当多一个指向该对象的 shared_ptr,引用计数加一,相反减一,引用计数为 0 时,释放被指向的内存空间;weak_ptr 是为了解决 shared_ptr 强指向可能出现的循环引用导致的内存泄漏,和当被指向的对象被释放产生的悬垂指针。)
3.     自动释放池原理,可以嵌套吗,用什么数据结构实现的 (自动释放池帮助管理对象的生命周期,MRC 中创建的对象可以由自动释放池 retain,ARC 中自动释放池可以写作大括号表示它所管理的作用域,当 MRC 的自动释放池被抽干(drain),或离开 ARC 自动释放池的作用域,由自动释放池管理的对象被 release 一次(不一定引用计数清零释放对象,仅仅是减 1)。可以嵌套,用栈实现)
4.     Runloop (使线程不因为任务都执行结束或没有任务而退出,使线程在没有任务调度时等待。实现内核态和用户态间的切换。主线程的 Runloop 是默认开启的,子线程中也可以注册和开启 Runloop,实现子线程的保活。Runloop 不太清楚答得不好。
5.     inline 和 define 区别(对内联函数的处理发生在编译期,对宏定义的处理发生在预处理;内联函数能够提供类型检查,相比宏定义更加安全;内联函数只是给编译器在编译时提供优化建议,使内容短小简单的处理不以发生函数调用的方式进行,减少函数调用的寻找函数入口地址,保存现场以及返回时发生的开销,当内联函数过于复杂时,编译器会选择按照一般函数的方式编译,不会在调用的位置展开替换,而宏定义一定会展开替换)
6.     C++ 编译和预处理(预处理:将宏定义展开替换到代码中的指定位置,删除注释内容等;编译:词法和语法分析,代码优化
7.     词法分析和语法分析(词法分析是用确定的有限自动机根据下一字符决定跳转,判断最终可否达到终止状态?语法分析是使用语法树?学过编译技术,记不清了,答得不好。)
8.     C++ 动态链接静态链接 (发生时期不同,动态链接具有更好地代码复用性,但是跨平台通用性差一些,能瘦身可执行文件,静态链接相反;动态库 XXX.a,静态库 XXX.lib) 
9.     OC runtime(OC 是高度动态的语言,OC 把类型确定,方法绑定,加载都可以推迟到运行时(runtime)进行,在变量定义时,等号右侧的类型在运行时才会被确认;在调用方法时,在运行时实例对象(类对象)会通过 isa 指针寻找到创建自己的类对象(元类),在类对象(元类)的结构中的 methodList 中寻找对应的实例方法(类方法),找不到方法时,还可以用 instance(class)MethodResolve 动态添加方法实现,还可以消息重定向,消息接受者重定向,解决当前类对象(元类)无法响应该消息的问题,如果也解决不了会通过 superclass 指针寻找父类,并重复这一处理过程;某些加载也可以发生在运行时,比如 category 对方法实现的扩展等)
10.   虚拟内存(虚拟内存是现代操作系统为每一个进程分配相互隔离的很大的内存空间(4G(32位系统)),各应用程序进程不是直接访问物理内存地址,而是访问虚拟内存地址,通过查询页表(多级页表,快表)查询到对应的虚拟内存,页表为页式管理的虚拟-物理内存建立映射关系。当首次读写 touch 到未建立映射的内存时,会触发缺页中断,CPU 由用户态陷入内核态,调用缺页中断处理程序分配实际物理内存并且建立内存映射,再由内核态返回用户态继续服务应用程序进程)
11.   C++ 程序从启动到运行到 main 函数(程序装入内存,分配并建立好内存模型各区域(栈,堆,全局,代码)的内存映射,寄存器记录当前栈底指针,寄存器异或取零,开始执行程序。答得不好)
12.   内存对齐(为了保证 CPU 能够一次性取出这些基本变量,64 位系统对 class A {int i, double d} 做 sizeof(A),返回大小不是 12 而是 16,如果是 12 那么取出 d 需要取两次,破坏取数操作的原子性。补全大小通常与 CPU 位数有关,32 位补齐 4 字节,64 位补齐 8 字节,也可以自定义设置)
13.   HTTP 协议工作原理(客户端与服务端建立 TCP 连接后,客户端发送 HTTP 请求,服务端回复 HTTP 响应)
14.   HTTP 头部(内容长度,可接受压缩类型(请求报文)/压缩类型(响应报文),catch-control,是否长连接等)
15.   HTTP 各版本(1.1:长连接与管道传输;2:并发传输(多个 stream 复用一个 TCP 连接,为解决队头阻塞做出的努力)、二进制传输(不光支持 ASCII 码内容传输,对消息类型适用性好)、头部压缩(静态表,动态表用索引值代替字段,哈夫曼压缩字段中的内容)、服务器主动推送;3:改 TCP-based 为 UDP-based(QUIC)(完全解决队头阻塞),用连接号标识连接(解决TCP用四元组标识通信在网络切换时发生的开销)
16.   HTTPS(密文传输,多一层安全套接字层,需要多做一组TLS握手,HTTPS建立到第一次发信的过程,基于离散对数(DH)的和基于大数质因子分解(RSA)的在是线上的区别)
17.   算法:数组中和为0的三元数

二面(9.30 45min)
1.     属性修饰符(第一个是 atomic 和 nonatomic,区分是否原子性线程安全,但 atomic 的效率低下,基本不用;第二个是 setter 方法的的修饰,MRC 下有 assign,retain,copy,assign 是正常的赋值操作,setter 方***把新值赋值给属性,retain 只能用作对象类型,使判断当前指针是否已经指向,如果没有则 release 一次后指向 retain 一次,copy 强调新拷贝出的与源的隔离,调用一次 copy;ARC 下还有 strong,weak,strong 与 retain 类似,会对引用计数加一,weak 不会,并在被指向的对象被释放时,变为nil)
2.     strong 和 copy 区别(strong 为被指向的对象的引用计数加一,copy 强调被拷贝出的新与源的隔离)
3.     copy 修饰 NSmutableXXX 会出现什么问题(copy 是不可改拷贝,当调用被 copy 出来的对象的修改方法时,会程序崩溃,因为查找不到对应的方法)。
4.     OC 消息查找机制(在调用方法时,在运行时实例对象(类对象)会通过 isa 指针寻找到创建自己的类对象(元类),在类对象(元类)的结构中的 methodList 中寻找对应的实例方法(类方法),找不到方法时,还可以用 instance(class)MethodResolve 动态添加方法实现,还可以消息重定向,消息接受者重定向,解决当前类对象(元类)无法响应该消息的问题,如果也解决不了会通过 superclass 指针寻找父类,并重复这一处理过程
5.     浏览器键入 url 会发生什么(首先做语法检查,无误生成对应的 HTTP 报文,接下来寻找通信五元组(传输层协议,源/目的端口号,源/目的IP),传输层协议 TCP,目的端口号 80,源 IP 地址由 DHCP 分配,目的 IP 地址通过查 DNS 缓存,如果无,再通过 DNS 递归查询(查询主体由 DNS 客户端转移到 DNS 本地服务器,先查根域名服务器,再查顶级域名服务器,(再查二级域名服务器,)再查权威域名服务器)获取),应用层工作结束。传输层建立 TCP 连接时,先用 socket(AF_INET, SOCK_STREAM) 返回一个套接字句柄 fd,客户端调用 connect(fd, remote_addr) 时,操作系统隐式绑定一个源端口号,至此五元组齐全,connect(...) 发送 SYN,并阻塞直到收到 SYN+ACK,服务端的监听端口调用 listen(fd,...) 监听到 SYN 后,调用 accept(...) 发送 SYN+ACK,并阻塞到收到 ACK,返回一个用于此连接的套接字句柄 fd,进入 established 状态,客户端收到服务端发送的 ACK,该套接字也进入 established 状态,TCP 连接建立完成并为 HTTP 报文打上 TCP 头部,至此传输层工作结束。网络层为报文打上 IP 头部,并根据目的 IP 地址查询路由表将消息发送到网关。数据链路层查询 ARP 缓存,如果无,通过 ARP 解析,获取下一跳的 mac 地址,并打上 mac 层头部。下一跳接收到消息后,解包到 IP 层,根据目的 IP 地址,查询路由表重复步骤,直到到达服务器。服务器网卡接收到信号后,解调为二进制数据,通过 DMA 将数据写入内存的缓冲区,完成 I/O 的前半部。完成后发中断同志 CPU 处理数据,CPU 解包到获取 HTTP 完整报文后,把内容写入套接字,应用程序根据不同的 I/O 策略(阻塞 I/O,非阻塞 I/O,异步 I/O,I/O 复用),将数据从内核态的 read() 或 recv() 到用户态,并处理。根据 HTTP 报文的请求,返回 HTTP 响应报文,按照前面所说的流程,返回给客户端。客户端根据 HTTP 响应报文中的内容,布局相关信息,渲染到浏览器页面中,展示给用户。)
6.     WEB服务器架构(负载服务器 + 应用服务器 + 数据库服务器 + 缓存,答得不好)
7.     纯文本结构如何布局(不了解)
8.     SQL 查询一个叫小明的语句(select * from table where name = “xiaoming”)
10.   CSS文件(只答出来布局 HTML 文件,不了解)
11.   JS文件(不了解)
12.   项目(难点,遇到什么困难,有什么收获)
13.   算法:无序数组中寻找一个数,左值比它小,右值比它大

三面(10.9 40min)
1.       项目,用什么实现的多线程同步(多线程用的C++ 11里的Thread,在主线程中创建子线程后 join,不让主线程提前退出;同步用的 semaphore.h 提供的面向过程的信号量)
2.       信号量和自旋锁区别(信号量主要用于实现线程同步,锁主要用于资源互斥;信号量处理同步是将未获取到资源的线程阻塞,这在小尺度时间调度上,需要较大的上下文切换开销,自旋锁在处理互斥是将为获取到资源的线程忙等不停询问,仍然分配时间片,在大尺度事件调度上开销大)
3.       C++怎么捕获崩溃(只答了try catch,throw,信号(SIGKILL,SIGSEV)处理,面试官问了 terminated Handler?不知道)
4.       非抢占式任务调度 应用场景(分时操作系统,批处理操作系统)
5.       Windows 操作系统是抢占式还是非抢占式(抢占式)
6.       实时操作系统和普通操作系统的区别(保证在某一固定时间内,高优先级的进程一定能够得到响应)
7.       如何定位不能上网的问题(先 ping 环路,检查协议栈是否有问题;查看 ipconfig;ping 网关;答得不好
11.    两个人玩抛硬币的游戏,谁先抛到正面获胜,那么甲获胜的概率?((1/2 + 1/2+1/2 + (1/2)^3 + ... + (1/2)^n ...的无穷级数,2/3)
12.    算法:字符串大数相减

HR面(10.11 25min)次日已OC

 类似资料: