美文网首页iOS高级开发面试题整理iOS面试题iOS面试
一颗不甘平凡的心之跳槽面试总结(有酷狗iOS经历)

一颗不甘平凡的心之跳槽面试总结(有酷狗iOS经历)

作者: 提呐个莫 | 来源:发表于2018-11-03 22:01 被阅读2次

    前言

    俗话说得好,no zuo no die,真的是不作一下,都不知道自己是怎么死的。
    本人iOS4年经验,坐标广州,前后换过3个公司。
    第一家公司让我转型成了iOS开发,开始了这条“不归路”。
    第二家公司是一家创业公司,也是在这家公司让我成长到了现在这个模样。在这家公司经历了太多起起落落,最后因为公司转型,无奈离场。但是在这公司的两年里,结识了许多非常不错的小伙伴,还是很有收获的,在此还是表以感激之情。
    第三家公司是一家算比较大(中型?)的公司吧,在这里开始体会到了大公司的制度。( 代码规范,代码规范,代码规范!划重点
    三个月后离职,主要原因:

    1. 从一家创业公司作为iOS端负责人(虽然只有两到三个人)进入大公司成了一颗小螺丝钉,心里落差还是很大的,有一种非常压抑的感觉。
    2. 代码提交需要审核,合并权限在于负责人。
    3. 项目主要基于webView开发,个人感觉没有太大发展(毕竟我是个有追求的人,追求技术深度,并非业务广度)。
    4. 公司开始推RN项目(重写现有项目),这是个大前端趋势,我并不反对。但是...需要自己去看代码熟悉前端H5、后台的业务...这就有点接受不了了!
    5. 每天写日报,记工时,绩效每月一次(填表感觉在论文,需要各种举证说明,简直吐血...)。福利一般,12薪,没有年终。

    于是,迫于一颗不甘平凡的心,就开始了离职找工作的作死行为。

    正文

    第一家公司就去了酷狗,在这里奉劝大家,在没有作为准备的提前下,千万千万千万不要去自己想去的大公司面试。如果,像我一样,离职之后第二天就去了酷狗面试,那最终结果就是:被虐得很残!
    不过,虐归虐,还是收获颇多的。
    来来来,接下来,给大家介绍一下酷狗面试流程(有即将要去或想去酷狗的童鞋们,快拿小本本来记笔记了)。
    酷狗每轮面试必须达到要求才能进入下一轮。

    1. 在线测试题,个人成绩80分。(选择题+数据缓存设计题,总分100,及格60。答完之后,显示的成绩只是选择题成绩。
    2. 逻辑测试,成绩未知,个人感觉应该挺高的。(总分100,及格80。去之前可以网上刷下经典逻辑测试题库,反正我是刷到了几个现成题。最后一道题是扫雷,9*7方格,根据已知信息,标出6个地雷所在位置
    3. 技术面,在此终结了,也是预料之中的事!
      我面试的岗位是酷狗直播(我之前有过一些音、视频的简单处理经验),网络协议相关的知识点是我的弱项,面试过程问了很多相关知识点,结果可想而知了。
    4. 据说后面还会有一轮技术面,然后再是HR面。

    整个面试过程,感觉面试官还是不错的。他会根据过往经历问到相关知识点,会循序渐进地问得很深。基础功是否扎实,基本都能在交谈过程中暴露出来。

    面试中问到的问题,记录如下(部分为引申出来的知识点):
    1. 手势的传递过程、响应过程。
    手势的传递是从上到下(父控件到子控件)。
    事件的响应是从下到上(顺着响应者链条向上传递:子控件到父控件。

    2. HTTP和HTTPS的区别:
    https协议需要到ca申请证书,一般免费证书很少,需要交费。
    http是超文本传输协议,信息是明文传输,https 则是具有安全性的ssl加密传输协议。
    http和https使用的是完全不同的连接方式用的端口也不一样,前者是80,后者是443。
    http的连接很简单,是无状态的。HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。

    3. http请求中的8种请求方法:

    1. opions 返回服务器针对特定资源所支持的HTML请求方法 或web服务器发送*测试服务器功能(允许客户端查看服务器性能)
    2. Get 向特定资源发出请求(请求指定页面信息,并返回实体主体)
    3. Post 向指定资源提交数据进行处理请求(提交表单、上传文件),又可能导致新的资源的建立或原有资源的修改
    4. Put 向指定资源位置上上传其最新内容(从客户端向服务器传送的数据取代指定文档的内容)
    5. Head 与服务器索与get请求一致的相应,响应体不会返回,获取包含在小消息头中的原信息(与get请求类似,返回的响应中没有具体内容,用于获取报头)
    6. Delete 请求服务器删除request-URL所标示的资源*(请求服务器删除页面)
    7. Trace 回显服务器收到的请求,用于测试和诊断
    8. Connect HTTP/1.1协议中能够将连接改为管道方式的代理服务器
      http服务器至少能实现get、head、post方法,其他都是可选的

    4. GET、POST请求有什么区别?POST是否可以完全代替GET:
    GET 请求可被缓存,POST 请求不会被缓存。
    GET 请求保留在浏览器历史记录中,POST 请求不会保留在浏览器历史记录中。
    GET 请求可被收藏为书签,POST 不能被收藏为书签。
    GET 请求有长度限制,POST 请求对数据长度没有要求。
    GET把参数包含在URL中,POST通过request body传递参数。
    GET 速度一般比 POST快。
    GET 请求不应在处理敏感数据时使用。
    重复的交互,比如取个数据,跳个页面, 用GET。
    不可以重复的操作, 比如创建一个条目/修改一条记录, 用POST, 因为POST不能被缓存,所以浏览器不会多次提交。
    GET产生一个TCP数据包;POST产生两个TCP数据包。
    对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
    而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。

    5. HTTP、Socket、TCP
    TCP连接和HTTP连接的区别
    HTTP基于TCP

    TCP连接与Socket连接的区别
    基于TCP协议的Socket连接同样需要三次握手建立连接, 是可靠的
    基于UDP协议的Socket连接不需要建立连接的过程, 不管对方能不能接收到都会发送过去, 是不可靠的, 大多数的及时通讯IM都是后者

    HTTP连接和Socket连接的区别
    HTTP是短连接, Socket(基于TCP协议的)是长连接
    HTTP连接服务端无法主动发消息, Socket连接双方请求的发送先后限制

    什么时候该用HTTP, 什么时候该用Socket
    用HTTP的情况: 双方不需要时刻保持连接在线, 比如客户端资源的获取、文件上传等
    用Socket的情况: 大部分及时通讯应用(QQ、微信)、聊天室、苹果APNs等

    iOS中, 发HTTP请求一般用原生的 NSURLConnection、NSURLSession、AFNetworking(推荐)
    连接Socket连接,可以用CocosAsyncSocket.

    6. 基本HTTP协议的断点下载(FTP断点下载,暂时没用到
    简单的断点续传实现大概如下:

    1. 客户端下载一个1024K的文件,已经下载了其中512K
      网络中断,客户端请求续传,因此需要在HTTP头中申明本次需要续传的片段:Range:bytes=512000-
      这个头通知服务端从文件的512K位置开始传输文件
      服务端收到断点续传请求,从文件的512K位置开始传输,并且在HTTP头中增加:Content-Range:bytes 512000-/1024000
      并且此时服务端返回的HTTP状态码应该是206,而不是200。

    2. 如何获取被下载文件的总字节数
      这里我们需要用到http 头部的conten-length字段。
      Content-Length用于描述HTTP消息实体的传输长度the transfer-length of the message-body。在HTTP协议中,消息实体长度和消息实体的传输长度是有区别,比如说gzip压缩下,消息实体长度是压缩前的长度,消息实体的传输长度是gzip压缩后的长度。
      简单点说,content-length表示被下载文件的字节数。
      有了这两个值,我们就可以算出下载进度了。

    3. 如何判断是否为同一个下载请求。
      我们需要把每个被下载文件的总字节数存储起来,这里我们选择使用plist文件来记载,plist文件包含一个字典。使用下载url作为key。

    7. Runtime的使用

    1. MJExtesion中的JSON解析。
      主要用到知识点:
    // 获取属性列表
    objc_property_t *properties = class_copyPropertyList(c, &outCount); 
    // 通过KVO赋值
    [object setValue:value forKey:self.name];
    // 类的反射
    NSString *key = NSStringFromClass(c);
    // 其它更详细的,可以自行学习源代码
    
    1. Method Swizzling黑魔法
      例子:AvoidCrash
        //主要
        Method method1 = class_getClassMethod(anClass, method1Sel);
        Method method2 = class_getClassMethod(anClass, method2Sel);
        method_exchangeImplementations(method1, method2);
    
    1. Category添加属性
    // 利用runtime绑定getter方法
    objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
    // 利用runtime绑定setter方法
    objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,
                             id _Nullable value, objc_AssociationPolicy policy)
    

    8. Method Swizzling加载时机
    Swizzling应该总在+load中执行
    在OC中,Runtime会在类初始加载时调用+load方法,在类第一次被调用时实现+initialize方法。由于Method Swizzling会影响到类的全局状态,所以要尽量避免在并发处理中出现竞争情况。+load方法能保证在类的初始化过程中被加载,并保证这种改变应用级别的行为的一致性。
    要使用dispatch_once执行方法交换
    方法交换要求线程安全,而且保证在任何情况下只能交换一次。

    9. Method Swizzling原理
    Method Swizzing是发生在运行时的,主要用于在运行时将两个Method进行交换,我们可以将Method Swizzling代码写到任何地方,但是只有在这段Method Swilzzling代码执行完毕之后互换才起作用。

    上图中selector2原本对应着IMP2,但是为了更方便的实现特定业务需求,我们添加了selector3IMP3,并且让selector2指向了IMP3,而selector3则指向了IMP2,这样就实现了“方法互换”。

    在OC语言的runtime特性中,调用一个对象的方法就是给这个对象发送消息。是通过查找接收消息对象的方法列表,从方法列表中查找对应的SEL,这个SEL对应着一个IMP(一个IMP可以对应多个SEL),通过这个IMP找到对应的方法调用。

    在每个类中都有一个Dispatch Table,这个Dispatch Table本质是将类中的SELIMP(可以理解为函数指针)进行对应。而我们的Method Swizzling就是对这个table进行了操作,让SEL对应另一个IMP

    10. NSThread、GCD、NSOperation的区别

    1. NSThread
      轻量级的线程操作,需要我们自己创建线程,调度任务,销毁线程
    2. GCD
      基于C语言
    3. NSOperation
      纯OC代码 操作队列,对GCD的封装.它是一个抽象类,只能使用其子类对象。系统提供了两个子类对象,分别是 NSInvocationOperation 和 NSBlockOperation。通常我们自定义 NSOperation 的子类,重写子类的 main 方法,把需要在分线程执行的任务放在 main 方法里。然后把 NSOperation 对象添加到 NSOperationQueue 中,就会自动在分线程执行 main 方法。

    NSOperationQueue和GCD区别联系
    区别
    NSOperationQueue没有串行/并发队列,但可以设置最大并发数;
    NSOperationQueue支持方法和block,GCD只支持block;
    NSOperationQueue可以暂停/取消操作;
    NSOperationQueue支持更多的功能,比如KVO和自定义操作;
    NSOperationQueue可以设置队列/操作的优先级,GCD只能设置队列的优先级。

    联系
    提供的功能是相似的;
    NSOperationQueue是GCD的封装。

    11. 死锁
    定义:
    所谓死锁,通常指有两个线程T1和T2都卡住了,并等待对方完成某些操作。T1不能完成是因为它在等待T2完成。但T2也不能完成,因为它在等待T1完成。于是大家都完不成,就导致了死锁(DeadLock)。
    产生死锁的条件:
    产生死锁的四个必要条件:
    (1) 互斥条件:一个资源每次只能被一个进程使用。
    (2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
    (3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
    (4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
    这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之
    一不满足,就不会发生死锁。

    12. 可能发生死锁的情况
    在某一个串行队列中,同步的向这个串行队列添加block。

    // 这个函数会把一个block加入到指定的队列中,而且会一直等到执行完blcok,这个函数才返回。
    // 因此在block执行完之前,调用dispatch_sync方法的线程是阻塞的。
    dispatch_sync(dispatch_get_main_queue(), ^(void){
                NSLog(@"这里死锁了");
            });
    

    结论:1.异步执行block肯定不会发生死锁。 2.同步的向 并发队列 中添加block不会导致死锁。

    视频直播中的弹幕系统,该怎么设计模块
    预留位置,想到答案了再来补吧...

    其它一些比较经典容易犯错的面试题

    下面关于线程管理错误的是:B
    A. GCD所用的开销要比NSThread大
    B. 可以在子线程中修改UI元素
    C. NSOperationQueue是比NSthread更高层的封装
    D. GCD可以根据不同优先级分配线程

    理由:首先,UI元素的更新必须在主线程。
    GCD与Block配合使用,block需要自动捕获上下文变量信息等,因此需要更多的资源,故比NSThread开销要大一些。
    NSOperationQueue与NSOperation配合使用,比NSThread更易于操作线程。
    GCD提供了多个优先级,我们可以根据设置优先级,让其自动为我们分配线。
    串行队列
    设置了优先级后,队列不一定是按FIFO规则,出队的顺序按优先级规则。
    并行队列
    并发队列执行任务的顺序是不确定的。对于同一优先级的任务,他们出队的顺序一定是FIFO,先进先出,但是先执行的顺序是不确定的!

    #define DISPATCH_QUEUE_PRIORITY_HIGH 2
    #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
    #define DISPATCH_QUEUE_PRIORITY_LOW (-2)
    #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    });
    

    下面关于Objective-C内存管理的描述错误的是:A
    A. 当使用ARC来管理内存时,代码中不可以出现autorelease
    B. autoreleasepool 在 drain 的时候会释放在其中分配的对象
    C. 当使用ARC来管理内存时,在线程中大量分配对象而不用autoreleasepool则可能会造成内存泄露
    D. 在使用ARC的项目中不能使用NSZone

    Objective-C有私有方法吗?有私有变量吗? C
    A. 有私有方法和私有变量
    B. 没有私有方法也没有私有变量
    C. 没有私有方法,有私有变量
    D. 有私有方法,没有私有变量

    理由:
    在Objective‐C中,所有实例变量默认都是私有的,所有实例方法默认都是公有的。
    1.OC中只有静态方法和实例方法。私有方法,可以通过延展(extension)实现,即在.m文件中声明与实现,只供内部使用,外部不能直接看到和使用,但是这不是真正意义上的私有方法。
    2.OC中有私有变量,通过@private声明私有变量

    下面代码的作用是让doSomeThing函数每隔1秒被调用1次。请问哪里有问题? A

    NSTimer *myTimer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(doSomeThing:)  userInfo:nil repeats:YES]; 
    
    [myTimer fire]
    

    A. 没有将timer加入runloop
    B. doSomeThing缺少参数
    C. 忘记传递数据给userInfo
    D. myTimer对象未通过[[myTimer alloc] init]方法初始化

    简述runloop和线程有什么关系
    每个线程,包括程序的主线程(main thread)都有与之相应的run loop对象。
    runloop和线程的关系:主线程的run loop默认是启动的,子线程的runloop默认是不开启的,需要我们自己手动开启循环。
    处在当前线程就只能操作当前线程的runloop。但可以给其它runloop发送消息。
    runloop同一时间只能处理一种runloopMode,其它mode无法响应。
    runloop执行完毕之后,就会进入休眠,只有在某个情况下触发了,才会再次调用。

    runloopMode

    自动释放池是什么?如何工作的?
    自动释放池是cocoa提供的帮助我们管理对象内存的一个工具。当我们像一个对象发送autorelease消息时,这个对象就自动加入到最新的自动释放池中,当自动释放池被销毁的时候,会自动向自动释放池中的所有对象发送一条release消息。也就是说我们不再需要手动向每一个对象发送release消息以释放对象,而是将其加入到自动释放池中最后统一释放。使用自动释放池也可以避免一些人为原因导致的内存泄漏。

    在实际开发过程中,什么情况下需要创建自动释放池?
    官方文档中有2种情况:
    If you write a loop that creates many temporary object.
    循环中创建了许多临时对象,在循环里使用自动释放池,用来减少高内存占用。
    If you spawn a secondary thread.
    开启子线程的时候要自己创建自己的释放池,否则可能会发生内存泄漏。

    简述UIView与CALayer有什么关系和区别

    1. 首先UIView可以响应事件,Layer不可以。
    2. UIView主要是对显示内容的管理,而CALayer 主要侧重显示内容的绘制。
    3. 在做 iOS 动画的时候,修改非 RootLayer的属性(譬如位置、背景色等)会默认产生隐式动画,而修改UIView则不会。
      对于每一个 UIView 都有一个 layer,把这个 layer 且称作RootLayer,而不是 View 的根 Layer的叫做 非 RootLayer。我们对UIView的属性修改时时不会产生默认动画,而对单独 layer属性直接修改会,这个默认动画的时间缺省值是0.25s。

    用递归函数,实现栈中元素的逆序!(不能用其它数据结构,如新建栈)
    以下答案为个人理解,并非标准答案,如有错误,欢迎指出互相交流学习。

    // NSMutableArray *array = @[@5, @4, @3, @2, @1].mutableCopy;
    - (void)reverseArray:(NSMutableArray *)array start:(NSInteger)start end:(NSInteger)end
    {
        if (start > end) {
            return;
        }
        if (array.count <= start || array.count <= end) {
            return;
        }
        NSNumber *temp = array[start];
        array[start] = array[end];
        array[end] = temp;
        [self reverseArray:array start:++start end:--end];
    }
    

    iOS中开发中,常常用到UIImageView来显示图片,但是是如何展示出来的?
    iOS从磁盘加载一张图片,使用UIImageVIew显示在屏幕上,需要经过以下步骤:

    1. 从磁盘拷贝数据到内核缓冲区
    2. 从内核缓冲区复制数据到用户空间
    3. 生成UIImageView,把图像数据赋值给UIImageView
    4. 如果图像数据为未解码的PNG/JPG,转码为位图数据
    5. CATransaction捕获到UIImageView layer树的变化
    6. 主线程Runloop提交CATransaction,开始进行图像渲染
      6-1如果数据没有字节对齐,Core Animation会再拷贝一份数据,进行字节对齐。
      6-2 GPU处理位图数据,进行渲染。

    未完待续...

    相关文章

      网友评论

        本文标题:一颗不甘平凡的心之跳槽面试总结(有酷狗iOS经历)

        本文链接:https://www.haomeiwen.com/subject/irgcxqtx.html