美团点评 iOS 校招面经

January 19, 2020
iOS开发

年前11月份的时候面试了美团点评的 iOS 开发校招岗位,现在来写一下面经,因为太过久远了所以记得不太清了,大概就是下面这么多了。

拿这篇文章记录一下,就当是写一篇iOS开发基础相关的文章吧,当了这么久独立开发者却没有好好的去记录过iOS相关的知识。不过我不会写的特别详细,很多东西只会大概说一下,想要详细了解的还请大家去网上查专门写那个知识点的文章。

Swift的新特性

因为WWDC2019 刚结束的缘故所以被问到了 Swift 的新特性,其中重量级的有Combine,Swift UI等等。
Combine是一个为Swift提供的响应式开发框架,引用文档上的解释:

The Combine framework provides a declarative Swift API for processing values over time. These values can represent many kinds of asynchronous events. Combine declares publishers to expose values that can change over time, and subscribers to receive those values from the publishers.

关于Combine具体的特性,WWDC有一些很好的视频可以学习:
Introducing Combine - WWDC 2019 - Videos - Apple Developer
Combine in Practice - WWDC 2019 - Videos - Apple Developer
也可以去查看官方的文档:Combine | Apple Developer Documentation

iOS 的内存管理

这样的问题在面试中也经常问到,感觉一般都是第一个问题,如果是需要准备 iOS 面试的话最好去了解一下。总体来说就是 iOS 有两种内存管理方法,一个是 ARC自动引用计数,一个是MRC手动引用计数。
在 MRC 的时代每次 init 一个对象,开发人员还要操心对象的生命周期,需要记住 strong、weak、retain、assign、copy、unsafe_unretained 这些关键字的意思。还需要知道 nonatomic与atomic 的意义等等,比如 atomic 原子性是线程安全的 iOS 底层使用了自旋锁来确保变量的原子性。还有的 autoReleasePool等等,如果再接着问autoReleasePool的原理的话就是 RunLoop 那边的知识了。

到了ARC的年代iOS可以自己管理内存,但是代码写的不好就会造成内存泄漏,关于 ARC 最经常问到的具体问题第一个就是什么是内存泄漏了。
iOS 在定义变量的时候有很多的关键字 strong, weak 是最常见的,在初始化变量时默认是strong。内存泄漏最常见的情况就是两个对象里面的 strong 变量相互持有自己,这种情况下当这两个对象被设为null之后因为两个对象还互相持有对方导致 ARC 不会释放他们的内存,而这两个对象在代码中却再也访问不到了,造成了内存泄漏。

还会问到的问题就是 Xcode 上怎么去查内存泄漏。关于内存管理的文章网上出色的文章很多,这里就不详细的说了
这里留下 Apple 的官方文档 Advanced Memory Management Programming Guide

struct和class的区别
在内存管理方面个人觉得还可能被问到,struct和class的区别,这时就得去了解值类型和引用类型的区别,以及是在堆内存还是栈内存之类的。简单的说就是struct是一个值类型,class是一个引用类型。在如下代码中 s1和s2内存地址可不一样,也就是说s1和s2不是一个引用,但是p1和p2确是相同的引用。

  1. struct Student{}
  2. var s1 = Student()
  3. var s2 = s1
  4. class Person{}
  5. var p1 = Person()
  6. var p2 = p1

问到这个之后紧接着就是深拷贝浅拷贝的理解了。需要理解 Copy 和 MutableCopy 的知识了。这也是非常重要的一个点,如果不理解这些开发的时候会踩很多坑。

这里放一个官方的文档 Choosing Between Structures and Classes | Apple Developer Documentation
文档中很好的解释了什么时候选择 struct 或者 class。

weak 关键字的实现

当时好像还被问到 weak 关键字是怎么实现的,这个没有具体的看过,不过当时猜了一个底层有一张表来维护变量和地址。没想到还说的差不多对了hhhhh。具体的可以去网上找别人的文章。

frame 和 bound 的区别

Frame 和 bound 的坐标系是什么,bound 有什么用 ,如果 bound 比 frame 大那么会不会显示,点击会相应吗,是谁参与响应链条的事件响应。

Frame是父View上看到子View的范围,Bound是子View上可以被父View看见的内容
个人认为对 frame 和 bound 最好的教程就是 ScrollView 的实现了,很早之前在 ImportNew 看过一个讲 ScrollView 的文章就说道 ScrollView 的滚动出现不同的内容其实就是通过调整自身的 Bound 实现的。
找了一个大致的文章:iOS开发-探索scrollView的实现 - 简书

App 数据持久化一般用什么

这个也是常见的问题 ,键值对储存一般使用 UserDefault, UserDefault是和储存一些非结构化的数据比如应用的设置之类的。
数据库那边 iOS 自带了 CoreData,我经常使用的持久化方案是 SQLite。然后被问到SQLite和CoreData有什么区别,不过因为本人对CoreData非常不熟悉所以没有答上来。后来才知道 CoreData 只是一种 ORM 不能算是数据库。
放一个链接供参考:What Is the Difference Between Core Data and SQLite

响应链条

UIKit 相关的问题,响应链条的原理也经常被问到,响应链条描述了你的在屏幕上的触摸一下再App一层一层的View中是怎么传递的。
响应链条的一个主要函数就是 hitTest(withEvent), 需要知道这个函数的原理,比如View在什么情况下不参与响应 (hidden = true,userInteractionEnabled = false,alpha < 0.01的时候)

在这里放一个官方的文档
Using Responders and the Responder Chain to Handle Events | Apple Developer Documentation

一个 View 绑定多个手势的冲突问题

没听懂题,应该是 UIGestureRecognizer 相关的问题

ViewController的生命周期

iOS 开发中非常基础但是新手又会常常忽略的问题,因为每次新建 ViewController 的时候Xcode会自动复写viewDidLoad函数和一个didReceiveMemoryWarning函数,这就使得很多新人只会在 viewDidLoad 中去更新 UI 界面,所有的东西都写在里面,这样写表面是没有问题的,ViewController 肯定能用,但是会引发一些界面上的更新问题,比如我的 ViewController 显示了一个列表,然后App显示了一个新的 ViewController 用于向列表添加一个项目,当用户退出这个ViewController 的时候,原本ViewController 的列表不会被刷新,原因就是因为 viewDidLoad 只会调用一次,应该在 viewWillAppear 中来编写列表更新的相关逻辑。
具体ViewController 的生命周期可以去看 Apple 的官方文档,里面解释的很详细了 Start Developing iOS Apps (Swift): Work with View Controllers

当然还需要了解 AppDelegate 的周期,也就是 App 的生命周期,用来监听 App 是否因为用户按下Home键而退出前台或者 App 在后台中又回到前台。关于这些官方文档也已经说明的很清楚了:UIApplicationDelegate - UIKit | Apple Developer Documentation

多线程问题

多线程有哪些
有四种,但是我主要会用到 GCD 和 NSOperation

  • pThread
  • NSThread
  • GCD
  • NSOperation

GCD 队列,串并行
GCD的几种模式要很清楚,GCD可以创建串行队列和并行队列与默认的主队列,同时又有异步执行和同步执行两种模式。所以就有四种不同的组合方式了。

生产者消费者模型用GCD怎么办
被问到当一个生产者生产了若干个产品,多个消费者要异步消费产品,使用GCD有什么解决方案
这个我回答使用 DispatchSemaphore 信号量模型。

GCD和NSOperation的区别,什么时候用GCD什么时候用NSOperation
这个是一个比较应用的问题,需要在开发同时使用过 GCD 和 NSOperation 才能答出来,我个人使用 GCD 的时候大多数是用来异步执行小的耗时任务,然后再切换回主队列来进行 UI 更新。对于大的网络任务,比如 Listify 的云同步,我会使用 NSOperataion 来做,因为代码更模块化一些,而且 NSOperation 是可以取消的
关于 NSOperation 的实践可以看我的文章:Listify 的iCloud云同步功能开发笔记

NSOperation 取消是真的取消了吗
当你答出 NSOperation 是可以取消执行的时候就会被问到是不是真的会取消,答案是不对的,NSOperation 类里面有一个 canceled 属性,当调用 cancel 函数的时候只是将这个属性设为 true,具体的取消逻辑需要开发者自己判断这个 canceled 属性来编写。

官方文档的解释:

This method does not force your operation code to stop. Instead, it updates the object’s internal flags to reflect the change in state. If the operation has already finished executing, this method has no effect. Canceling an operation that is currently in an operation queue, but not yet executing, makes it possible to remove the operation from the queue sooner than usual.

什么是死锁以及死锁是怎么产生的
这个没什么好解释的,接触过多线程的人都会知道

其他的
关于多线程其实能问的问题非常多,比如 NSOperation 的操作依赖啊,GCD 的栅栏和队列组等等。

iOS 中传递消息的方式

我回答了我常用的三种 NotificationCenter, Protocol和KVO
然后就会被问到什么时候用什么。
NotificationCenter 相当于一种观察者模式,我经常用在两个互不相关的模块通讯的情况。
Protocol 我使用最多,相当于 Java 的接口,因为日常写 Java 很多而且受到以前 Android 的影响所以 Protocol 用的多一些。
KVO 一般是用来监听值得变化,用来更新UI。
然后被问到有没有使用过 RxSwift 之类的。

KVO怎么实现的

因为之前说过 KVO 了所以我也猜到会问 KVO 是怎么实现的,想要答出来这个问题就得对 iOS 的 Runtime 了解一些。主要原理就是KVO会为监听对象派生出来一个新类,通过更改原对象的 ISA 指针来实现 (ISA Swizzling),并且重写 set 和 get 函数,添加一些逻辑用于通知,在这里不细讲了。关于 Rumtime 的讲解也是有很多人都写过,很多。虽然我日常用 Swift 也用不到这些,但是Runtime里面的 NSObject 结构啊,消息转发去了解一下也没有什么坏处。

基本的网络问题

解释一下 https,SSL之类的
如果要答的话我会先说非对称加密的原理,因为以前专门度过 RSA 的论文在学校做过演示所以当时可以说的很详细。而且自己网站的 SSL 也是我自己集成的所以答的也没有问题,具体可以看我的文章 SSL证书申请与Spring Boot集成

TCP UDP
TCP和UDP的基础也是是个面试只要问到网络基础就会出现的问题。
TCP 的三次握手与四次挥手之类的,不过当时好像也没有问的特别细,TCP滑动窗口啊拥堵控制啊个人觉得也是挺重要的知识。UDP与TCP的区别也会被问。

POST和GET什么区别
这个也是经典问题了,需要答出两者的作用,参数,还有 GET 方法是安全的,POST会改变服务器状态所以不是安全的,还有就是两者的幂等性, POST 方法不是幂等的。

举几个常见的UDP协议
我提到了 DNS 应用,然后被问到 QUIC 是一种 TCP 还是 UDP 协议,我也是后来才知道 QUIC 是 Quick UDP Internet Connection 的意思, 不过当时蒙对了,毕竟很多TCP的协议都有了解过或者用过。

当在浏览器网址输入之后按下回车之后都发生了什么
这个还是看个人网络基础知识掌握多少了,我回答了路由器如何判断是本机 IP 还是外网 IP,提到路由器肯定就要再提到 ARP 协议,然后连接 DNS 服务器查出来网址的 IP 地址,建立TCP链接,浏览器渲染等等等等,可以说的很细也可以说的很浅。

Http攻击方式
不知道为什么被问到了这个,挺诧异的,当时回答了一个中间人攻击,因为以前也写过文章所以记得比较清楚:论如何黑掉舍友的网络账号
之后还被问到有没有抓过包啊之类的,我大概回答了一下 WireShark 的使用。

Http状态码
毕竟写过后端,说几个状态码还是妥妥的,要是只知道404那真的是要没办法了。
状态码 1XX 系列到 5XX 系列都要大概记住。
最起码要知道 1系列是信息类状态码,2是成功状态码,3是重定向相关的,301和302很常用,4系列是客户端错误状态码,5系列是服务器错误状态码。

应用程序的性能评估

被问到如果让你设计一个平台,你会从那几个方面去评估 App 的性能和稳定性,这个我回答的很费力因为平时独立开发很少考虑这个方向,不敢乱说。

项目中的难点

这个不同的人也不一样,可以从 UI 适配方面,项目解耦,或者算法性能瓶颈等方面说一说,毕竟每个人开发的时候都会遇到困难。

UILabel 如果想把字从右向左显示怎么办

因为不是所有国家都和中国一样字体都是从左向右表示的,所以被问到这个问题,很遗憾没有什么头绪,满脑子想的是自己实现一个 UILabel 算了。

如何不重新启动切换语言

这个我没有自己实现过,但是知道要使用 NSBundle,但是仅此而已。

举几个常见的设计模式

送分题,设计模式十多种说出来五六种也不是不可以。
就我个人而言单例模式一定是最先说到的,因为经常用到,再深入一点就是关于单例模式线程安全的实现了。
还有在 UI 部分经常用到的适配器模式,在 Android 上尤为明显,ListView 的列表Cell在UI上表现出的不同就是因为 Adapter 不同。还有常用的观察者模式,装饰器,工厂模式等等。

之后还被问到 ViewController 的生命周期用的是什么模式,这个是第一次被问到,就是说 viewDidLoad 那些函数是一种什么设计模式,这个我不确定,回答了观察者模式。
设计模式

UI性能优化

离屏渲染,圆角,阴影等等,因为自己没有遇见过特别多,也是按照平时看过的文章按照记忆答的,不能说的太细。

如何估算城市里有几个公交车

这个问的也是很诧异,因为是很多商科公司面试的题目,以前听说过被问如何估算一个城市里面有多少个网球的,和那个比起来公交车还算容易的问题了。应该是考你的发散性思维和逻辑,要是卡着什么都不说就输了。

最近看了什么书

这个不同的人不一样哈哈哈哈,当时说了一个Docker,于是被问一些Docker相关的事情,不知道说其他的书面试官会怎么反应。

Comments

作者
July 21, 2018 at 10:52 am

There are no comments

keyboard_arrow_up