美文网首页iOS面试系列
记一次某公司iOS面试总结

记一次某公司iOS面试总结

作者: 温特儿 | 来源:发表于2018-04-19 00:24 被阅读17次

    昨天去了一公司面试,面试官问的都是些很基础的东西,觉得虽然基础,但却在工作中可能会有用到,所以特意记录下来。

    结合面试问题然后展出的一些相关的知识点~

    Q:"abcd"占多大空间,int类型占多大?

    A:
    "abcd" 占用5个字节,因为双引号括起的叫字符串,并含一个字符串结束符
    字符串长度strlen( "abcd")-》 4
    占内存 sizeof ("abcd") -》 5

    延伸:
    位:"位(bit)"是电子计算机中最小的数据单位。每一位的状态只能是0或1。
    字节:8个二进制位构成1个"字节(Byte)",它是存储空间的基本计量单位。1个字节可以储存1个英文字母或者半个汉字,换句话说,1个汉字占据2个字节的存储空间

    一般32位操作系统中 int占用4字节(Byte)/32位,char占1字节(Byte),short占2字节(Byte)

    Q:内存栈和内存堆的区别

    A:

    • 栈区(stack):由编译器自动分配释放,存放函数的参数值,局部变量等;
    • 堆区(heap):由程序员分配释放,若没有释放,程序结束后由系统回收;
    • 全局区(全局区)(static):全局变量和静态变量的存储放在一块,初始化的全局变量和静态变量在一块区域,未初始化的在相邻的另一块区域,程序结束后由系统回收;
    • 文字常量区:常量字符串放在这里,程序结束后由系统回收;
    • 程序代码区:存放函数体的二进制代码

    例子:

    //main.cpp
    int a = 0; 全局初始化区
    char *p1; 全局未初始化区
    main() {
      int b; 栈
      char s[] = "abc"; 栈
      char *p2; 栈
      char *p3 = "123456"; 123456/0 在常量区,p3在栈上。
      static int c =0;全局(静态)初始化区
      p1 = (char *)malloc(10);
      p2 = (char *)malloc(20);
      分配得来得10和20字节的区域就在堆区。
      strcpy(p1, "123456"); 123456/0放在常量区,编译器可能会将它与  p3所指向的"123456"优化成一个地方。
    }
    

    Q:进程和线程的关系

    A:

    • 进程是资源分配的最小单位,线程是程序执行的最小单位。

    • 进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。

    • 线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行。不过如何处理好同步与互斥是编写多线程程序的难点。

    • 但是多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。

    • 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。

    • 资源分配给进程,同一进程的所有线程共享该进程的所有资源。
      线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。

    • 处理机分给线程,即真正在处理机上运行的是线程。

    • 线程是指进程内的一个执行单元,也是进程内的可调度实体。

    Q:进程、线程、堆、栈的关系

    在一个进程中的线程中 共享堆区,而进程中的线程各自维持自己的堆栈
    (感觉这块说的有点太简单了,如果你有好的见解,不妨在下面👇评论区,写上你的看法,我会及时更新的)

    Q:HTTP在七层结构中的哪个层? HTTPS呢

    A:
    七层结构:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层

    TCP属于传输层,HTTP属于应用层

    HTTPS加密在传输层,传输层的功能包括是否选择差错恢复协议还是无差错恢复协议,以及同一主机上对不同应用的数据流的输入进行复用,还包括对收到的顺序不对的数据包重新排序

    HTTPS在数据传输过程中采用非对称加密方式
    对称加密:加密数据用的密钥,跟解密数据用的密钥是一样的
    非对称加密:加密数据的密钥(公钥),跟解密数据使用的密钥(公钥)不一样

    公钥放在客户端,密钥放在服务端,公钥加密的数据只有密钥能解开,私钥加密的数据也只有公钥能解开
    可信任的证书颁发机构CA颁发的证书里面包含了公钥信息

    HTTPS虽然用到了公开密钥加密,但同时也结合了其他手段,如对称加密,来确保授权、加密传输的效率、安全性

    整个简化的加密通信的流程就是:
    小明访问XX,XX将自己的证书给到小明(其实是给到浏览器,小明不会有感知)
    浏览器从证书中拿到XX的公钥A
    浏览器生成一个只有自己的对称密钥B,用公钥A加密,并传给XX(其实是有协商的过程,这里为了便于理解先简化)
    XX通过私钥解密,拿到对称密钥B
    浏览器、XX 之后的数据通信,都用密钥B进行加密

    注意:对于每个访问XX的用户,生成的对称密钥B理论上来说都是不一样的。比如小明、小王、小光,可能生成的就是B1、B2、B3.

    握手:证书下发,密钥协商
    数据传输:使用握手阶段协商的密钥加密

    参照 //www.ruanyifeng.com/blog/2014/02/ssl_tls.html

    Q:多线程使用问题

    A:
    Critical Section(临界代码段)
    指的是不能同时被两个线程访问的代码段,比如一个变量,被并发进程访问后可能会改变变量值,造成数据污染(数据共享问题)。

    Race Condition (竞态条件)
    当多个线程同时访问共享的数据时,会发生争用情形,第一个线程读取改变了一个变量的值,第二个线程也读取改变了这个变量的值,两个线程同时操作了该变量,此时他们会发生竞争来看哪个线程会最后写入这个变量,最后被写入的值将会被保留下来。

    Deadlock (死锁)
    两个(多个)线程都要等待对方完成某个操作才能进行下一步,这时就会发生死锁。

    Thread Safe(线程安全)
    一段线程安全的代码(对象),可以同时被多个线程或并发的任务调度,不会产生问题,非线程安全的只能按次序被访问。
    所有Mutable对象都是非线程安全的,所有Immutable对象都是线程安全的,使用Mutable对象,一定要用同步锁来同步访问(@synchronized)。
    互斥锁:能够防止多线程抢夺造成的数据安全问题,但是需要消耗大量的资源
    原子属性(atomic)加锁
    atomic: 原子属性,为setter方法加锁,将属性以atomic的形式来声明,该属性变量就能支持互斥锁了。
    nonatomic: 非原子属性,不会为setter方法加锁,声明为该属性的变量,客户端应尽量避免多线程争夺同一资源。

    Context Switch (上下文切换)
    当一个进程中有多个线程来回切换时,context switch用来记录执行状态,这样的进程和一般的多线程进程没有太大差别,但会产生一些额外的开销。

    Q:快速排序算法实现

    A:
    算法思路:基于递归实现

    • 选定一个合适的值(理想情况中值最好,但一般使用数组第一个值),称为“枢轴”(pivot);
    • 基于这个值,将数组分为两部分,较小的分在左边,较大的分在右边;
    • 一轮循环后,这个枢轴的位置一定在最终的位置上;
    • 对两个子数组分别重复上述过程,直到每个数组只有一个元素;
      排序完成。

    参考代码:

    void quickSort(int *arr, int _left, int _right) {
        if (_left >= _right)  return; // 左边的索引大于等于右边索引,代表已经整理完成一组
        int left = _left;
        int right = _right;
        int key = arr[left];
        while (left < right) { // 当left!<right时说明一轮扫描结束
            while (left < right && key <= arr[right]) {
                // 结束循环的条件是:找到第一个比key小的元素位置 && left<right
                right--; // 向左扫描
            }
            arr[left] = arr[right]; // 找到第一个比key小的元素,补key值的空缺位
    
            while (left < right && key >= arr[left]) {
                // 这是left在左侧组内向右扫描,同上,不过注意结束循环条件相反
                left++;
            }
            arr[right] = arr[left];
        }
        arr[left] = key; // 当组内扫描一遍完成后,key值回归
        quickSort(arr, _left, left-1);
        quickSort(arr, right+1, _right);
    }
    

    以上是本篇文章的所有内容~ 希望会对你有所帮助🙏

    相关文章

      网友评论

        本文标题:记一次某公司iOS面试总结

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