知识记录

2018-08-08  本文已影响0人  任丫丫么任

# 前言

---

> 在易鑫服务了一年多,因为种种原因,还是自己的原因最主要吧,选择了离开易鑫,开始寻找新的方向,在此期间收到了很多公司的面试,也不伐一些大厂,在此做一个总结,送给准备和我做一样or类似决定的同事们,感谢大家一年多的关照与教导。

我将会进我可能,按照面试的公司分别记录相应的面试题,面试题以基础-发散-算法的结构描述(也是面试的一般的面试套路),然后我会附上我找到的一些答案链接,或者说一下自己的见解,对于答案,大家见仁见智。在最后做一个概括性的总结,期间难免有所遗漏,还请大家见谅。

A lot of thanks!

# 面试题/公司(/部门)

---

## 美团点评-打车部门

### 基础

1. **Objc 动态性**

参考答案:**动态类型**、**动态绑定**和*动态加载*,[参考链接](https://blog.csdn.net/cordova/article/details/53876682);

2. **Objc Runtime**

* **Objc 内存布局**

参考答案:直接给出陈宜龙对于 sunnyxx 面试题的答案:[参考链接](https://github.com/ChenYilong/iOSInterviewQuestions/blob/master/01《招聘一个靠谱的iOS》面试题参考答案/《招聘一个靠谱的iOS》面试题参考答案(上).md#19-一个objc对象如何进行内存布局考虑有父类的情况);

* **消息机制(dynamic binding-动态绑定)**

参考答案:消息机制,相信大家都知道这个过程,具体我就不细说了,我的理解就是消息机制提供了静态语言例如(C、C++)提供不了的间接性,对此,苹果也做了相应的介绍,详见[苹果官方文档](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtForwarding.html#//apple_ref/doc/uid/TP40008048-CH105-SW1),具体章节忘记了,自行阅读;

* **分类方法加载的优先级**

参考答案:在[苹果开源网站上](https://opensource.apple.com)大家可以下载到一些*源码*,我猜测,这里的*源码*和线上的代码一定不同哈,不过也能够看出一些东西,可以解释这个问题。看一下runtime.h中对于Class的源码中,方法列表 methodLists 的定义可以看出,methodLists 是一个指向指针的指针(**pointer),在加载分类方法时,是倒插入这个列表中的,然而因为消息机制也是倒着找方法的,找到之后存入方法cache中,所以下次就不会再重新找,这样也就是分类的优先级高于原类的优先级的原因;

* **分类中添加属性**

参考答案:答案当然就是我们常用的**关联属性(Associate Objects)**,实际上应该是存储在一个全局的 hash table 中,具体请看[这篇](https://www.jianshu.com/p/79479a09a8c0),当然这个源码我还没有读到,大家感兴趣也可以自己看源码;

* **Weak 的实现**

参考答案:其实不论是 weak 或者是对象的引用计数信息都是存放在一张全局的 hash table 中,详细的话可以看看[这个](https://www.jianshu.com/p/ed43b17c8a72),当然这个我是随便搜的,网上找一找,特别多,如果这个写的不好,大家再搜搜别的;

3. **@property**

参考答案:我所理解的, @property = _ivar + getter + setter。具体内存管理语义修饰符相信大家也都知道,贴出一个[链接](https://blog.csdn.net/u014205968/article/details/64443443);

4. **Copy & MutableCopy**

参考答案:直接上[链接](https://www.jianshu.com/p/22affaf798f8)了,这个是我之前写的一点东西,大家看看哈,别喷我;

5. **Responder Chain(响应链)**

参考答案:响应链是[职责链模式](https://www.cnblogs.com/cxxjohnson/p/6403849.html)的一种具体实现吧,在 SpringBoard 接收到 touch 之后,将触摸打包成 UIEvent 发送给当前的 application,application收到之后事件之后,将决定如何响应这个触摸,也就是由谁来响应。大概为如下过程:

**a. 从下向上找到触摸的视图;**

**b. 从上到下找到处理触摸的 target,包括 UIReponder(UIView、UIControl等) 和 UIGesture;**

**c. 如果找不到,那么最后会尝试让 application 去处理;**

**d. 如果 application 还是无法响应,那么会让 app delegate 去处理(也就是常问的, appDelegate 是继承于 UIReponder 的);**

**e. 如果还是没有得到处理,就抛弃。**

其中,一般会问到,如果一个**视图超出了父视图的边界(bounds),如何使之响应**这种问题,一般来说就可以通过```- hitTest:withEvent: 和 -pointInside:withEvent:```这两个方法来处理。具体链接大家还是自己找吧,不贴了;

4. **block**

参考答案:乏善可陈,总而言之看[llvm 的block imp](http://clang.llvm.org/docs/Block-ABI-Apple.html#layout-of-block-marked-variables)吧;

### 发散

1. **MVC、MVVM、VIPER(可选,我是尝试过 VIPER)**

答案:这里我贴出两个链接,一个是 [casa 的系列文章](https://casatwy.com/iosying-yong-jia-gou-tan-viewceng-de-zu-zhi-he-diao-yong-fang-an.html),另一个是 [objccn 的 VIPER](https://objccn.io/issue-13-5/),见仁见智吧;

2. **如何处理忘记移除通知观察者的问题**

参考答案:我没有尝试过,也没想过具体怎么弄,大家自己查吧;

3. **CPU 流水线(可选)**

参考答案:自己查吧,我是看过相关方面的书;

4. **高速缓存(可选)**

参考答案:自己查吧,我是看过相关方面的书;

5. **友盟崩溃率计算方式等**

参考答案:我也不知道具体公式是啥,大家自己查吧..

### 算法

1. **洗牌算法,给你一个数组,打乱这个数组,有可用的随机数函数。**

参考答案:说一个大概的思路,就是每次随机从有序数组中抽取出一个元素,然后加到乱序数组中,把有序数组抽空,乱序数组就满了;

2. **使用 C 语言写一个 stack,考虑多线程**

参考答案:我是用链表实现的,这里大家随便找,有的是文章;多线程这个我只知道加锁,自己看咋弄吧...

3. **统计一个已知函数的运行时间,只允许在一处插入代码,考虑多线程**

参考答案:经过面试官提示,采用了递归的思路(如果需要考虑多线程的话,那么可以想办法把这个插入的 flag 和线程绑定),具体实现看demo:

```

/// 原函数

int test(int param)

{

/// 各种分支

if (param > 0) {

return param * param;

}

if (param == 0) {

return param;

}

return param / 3;

}

```

/// 插入统计时间的代码后

```

int test(int param)

{

///插入的代码

static int flag = 1;

if (flag) {

flag = 0;

clock_t start = clock();

int retVal = test(param);

clock_t end = clock();

flag = 1;

printf("Duration is %lu.\n", end - start);

return retVal;

}

/// 各种分支

if (param > 0) {

return param * param;

}

if (param == 0) {

return param;

}

return param / 3;

}

```

## 搜狗-金融部门

### 基础

1. **RunLoop**

参考答案:参考的 YYKit 作者的[深入理解 RunLoop](https://blog.ibireme.com/2015/05/18/runloop/),以及 [RunLoop开源源码](https://opensource.apple.com);

2. **内存管理**

参考答案:自己找吧,反正我看源码是存在 **side table** 中的,这块我面试的时候,我也不知道 **side table** 这种东西,也就说了 retain release 啥的,当然比如 globle object 、 static object 和 tagged pointer 不用做这些操作;

3. **锁(数据竞争问题处理方式)**

参考答案:参考官方文档的 [Thread Programming Guide 中的 Syncronization 一节](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html#//apple_ref/doc/uid/10000057i-CH8-SW1);当然还有我也才知道的 [CAS 乐观锁](https://blog.csdn.net/liubenlong007/article/details/53761730)等,当然我的理解不深,或者说我不会...

4. **Grand Central Dispatch**

参考答案:我在这里谈谈我个人的理解,当然我没有看源码...我的理解 dispatch_queue 是一个**任务队列**,我们在使用 GCD 的时候一般是把任务添加到队列中,队列要么是串行,要么是并发。那么串行队列语义就是顺序执行任务,并发队列顺序不确定。我的理解是这样的,串行队列是顺序取出内部添加的任务,然后派发到线程中去执行;并行队列也是顺序取出任务,但是执行顺序不确定,因为并发队列取出多个任务,然后派发到不同的线程中去,这些线程的执行顺序不确定。

* Extra: 在一个**串行队列**中派发了10个**任务**,那么这10个任务会在同一个线程执行吗?

参考答案:**不一定**。如果我先在队列中派发了一个任务,这个任务做完之后,队列发现没有要做的任务了,那么会把用到的线程归还到线程池中。再派发第二个任务的时候,队列发现有任务要做,但是没有可用的线程,队列会去线程池中取一个线程出来,这个时候,就出现了两个线程,而且有非常大的可能不是同一条线程了。所以对于串行队列我的理解是**语义上保证了任务的执行顺序,但是不一定是同一条线程**;

5. **KVO**

参考答案:[KVO原理分析及使用进阶](https://www.imooc.com/article/24023),我大概看了下,大家也可以自行查询哈,不保证一定是对的。大概的思路就是动态生成一个子类,然后去重写 setter(KVO setValue:forKey:也会处理),发送相应的消息出去;

6. **@property**

参考答案:重复问题;

7. **Copy & MutableCopy**

参考答案:重复问题;

### 发散

1. **怎么做一个 Crash 统计**

参考答案:自己搜吧...我知道的有两个点,一个是 ```NSSetUncaughtExceptionHandler``` ,但是这种只能处理 NSException 类型的异常;另一种就是 C 语言的函数 ```signal(int, void (*)(int))``` 去注册发生对应信号的异常处理函数,比如说定义在 signal.h 中的常量宏 SIGFPE 等,对应的也有很好的注释。其他的我暂时也不是很清楚了;

2. **有没有遇到过 CGRectNull 崩溃问题**

参考答案:我还真没遇到过这个具体的,不过我遇到过 ```CGRectInfinite```、以及 ```NaN (Not a Number)``` 发生的崩溃。在做车贷后台模糊效果的时候,有遇到过因为 webView 中的这种*非正常* 的 rect 在绘图过程中导致了崩溃,每个系统发生的问题,打印的错误也都不大一样,还是自行研究吧;

3. **老生常谈的 UITableView(UICollectionView)卡顿优化**

参考答案:大家自己查吧..我的理解是从两个层面考虑:**CPU卡顿**、**GPU**卡顿,在这二者之间维持一个平衡;如果怎么样都做不到平衡之后不卡顿,那么可以考虑比如 **缓存** ?用 **过渡动画** 等提高用户体验 ?在此贴出[表哥 Delpan 的 页面间跳转性能优化系列](https://www.jianshu.com/p/77847c0027c9);

### 算法

1. **10000个数中找出最大的3个数(Top(n)) 问题**

参考答案:网上针对于 **Top(n)** 问题好像有三种解决方案,我只知道一个,就是用**优先队列(小顶堆)**来解决,[贴个链接](https://blog.csdn.net/gaoranfighting/article/details/44784059);

2. **全世界有没有两个人的头发数量是相同的(WTF)**

参考答案:**有的**,意思是说只要人的数量远大于头发的数量,那么头发的假设头发的数量是100W,也就是头发数量是 0-100W,人的数量为 60 亿,那么就一定有两个人头发数量是相同的;反正我挂了...

3. **数组与链表**

参考答案:这个问题就比较简单了,相信大家都会..

4. **MVC、MVVM、VIPER**

参考答案:重复问题;

## 滴滴-顺风车

### 基础

>到了滴滴这边,好多就都是重复的了,而且我的本上也没有那么多东西..

1. **RunLoop**

2. **@property**

3. **NSCache**

参考答案:自己查咯,我也没说明白这个玩意儿,因为几乎不咋用..

...

### 发散

1. **如何设计一个图片缓存组件**

参考答案:这个说起来比较多,比如 URL-ImageFileName 映射,中间处理过程、内存以及硬盘缓存(这里有一个思想,就是高速缓存的思想,一般来说速度快的缓存,造价更高,容量也就比较小;比如内存造价比硬盘更贵,容量更小,所以内存是硬盘的高速缓存,以提高读取速度)、淘汰策略([FIFO 、LRU、LFU等](https://blog.csdn.net/clementad/article/details/48229243))...

### 算法

1. **打印菱形**

**Description:**  写一个函数 ```void printStar(unsigned int n)```,具体我插入一张图片吧:![描述图片](https://img.haomeiwen.com/i858510/cb202e066c0d1b53.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

参考答案:在这里我给出的答案如下:

```

static void _printStar(unsigned int cnt)

{

while (cnt) {

putchar('*');

cnt--;

}

}

static void _printWhiteBlank(unsigned int cnt)

{

while (cnt) {

putchar(' ');

cnt--;

}

}

static void _printReturn(void)

{

putchar('\n');

}

static void drawStars(unsigned int n)

{

unsigned int halfn; /// half n ..

unsigned int nos = 1, nob; /// number of stars, number of blanks

unsigned int idx = 0;

/// 如果 n 是偶数,那么 n 加等于 1,使之成为奇数

/// 这里我们并没有把这件事做的高效,如果需要高效,可以 |= 1 这种方式,效率应该比 % 高很多很多

if (n % 2 == 0) {

n += 1;

}

halfn = nob = n / 2; /// 这里也可以通过 >> 1 ,使之高效

/// 先打印上半部分

while (idx < halfn) {

_printWhiteBlank(nob);

_printStar(nos);

_printWhiteBlank(nob);

_printReturn();

idx += 1;

nos += 2;

nob -= 1;

}

/// 打印中间的 *

idx += 1;

_printStar(nos);

_printReturn();

/// 打印下半部分

nos = n - 2;

nob = 1;

while (idx < n) {

_printWhiteBlank(nob);

_printStar(nos);

_printWhiteBlank(nob);

_printReturn();

idx += 1;

nos -= 2;

nob += 1;

}

}

int main(int argc, char * argv[])

{

drawStars(7);

}

```

2. **已知有一个顺序数组,数组中放着 1, 2 ... n 的数字,我们在这个数组中随机抽取出两个数,然后打乱顺序,得到乱序后的数组 randArray;问:数组中抽取出的是哪两个数,怎么做,时间复杂度,空间复杂度。**

参考答案:我只有一个思路哈,我最近也没查。大概思路就是做一个桶,也就是说我们申请一个容量为 n 的数组 bucket,顺序从乱序数组中取数字,比如第一次取出 7,我们把 7 放到 bucket 的下标 7 中,再去一个 3 ,把 3 放到 bucket 的下标 3 中... 当取光了乱序数组之后,遍历桶 bucket,那么随机取出的两个数字就是桶中数字为 0 的下标。这种做法一共遍历了两次,时间复杂度为 O(n),空间复杂度为一个桶,也就是 O (n)。不过面试官说空间复杂度太高...更优解大家再查查,或者研究研究...

3. **有 25 匹马,5 条赛道,没有计时器,怎么样找出最快的 3 匹马..**

参考答案:面试官提示,这个是二维数组淘汰,于是我就画了一下二维数组,但是这个问题我也没试过...我当时给出的答案是跑 9 次,大概是先分五组跑,得出五组有序的数组,然后拿出一个标识数组的后两个,再拿另一个数组的前三个比赛,跑完后更新标识数组,再拿出下一组...依次类推,一共是跑了九次得到最快的一组,然后前三名就是最快的马了。不过面试官说可以优化的,浪费的信息比较多... 最优解大家还是查查吧;

4. **把一个单位长度为 1 的线段随机分成三份,能够构成三角形的概率是多少?

参考答案:我贴个链接吧,这个算是数学问题.. [参考答案](https://www.zybang.com/question/b1c54cd15031071b7d902710067a8109.html)。

### Other

1. **Weak实现**

2. **反转链表**

3. **Hash table**

# 总结

---

1.面试过程中的很多问题我都记不太清了,在这里把一些有印象的题目分享给大家,当然一些小公司的也有,不过因为面试体验渣等问题..就不多说了...

2.面试问到我网络,我都说我不会...我只知道都是 I/O...网络方面,大家自行搞定吧..

3.很多问题,其实在苹果开源的代码中都能得到具体的答案,不过我比较懒,没咋看,以后希望多看看,更希望大家也都多看看;

4. 希望大家读一读 [深入理解计算机系统](http://product.dangdang.com/24106647.html)这本书,这本书真的讲了很多内容,函数调用、信息表示、CPU、跳转、异常处理、多线程等,出去也有的吹,哈哈。然后再读一本 [深入解析Mac OS X & iOS 操作系统](http://product.dangdang.com/23453263.html),相信读完之后大家的技术也都精进了很多了..

5. 当然,我在这里更希望大家看很多东西的时候不要看是怎么做的,更多去思考一下为啥这么做... 出去面试就算说不清细节,也能白话一会儿。

# 结束语

> 感谢大家一年多的关心与照顾,无论我离职与否,我都会在心里记得大家,记得这一年多的光阴,希望大家都越混越好。泪奔...

上一篇 下一篇

猜你喜欢

热点阅读