Skip to content

Latest commit

 

History

History
248 lines (231 loc) · 17.6 KB

面试题-技术.md

File metadata and controls

248 lines (231 loc) · 17.6 KB

面试题

  1. 网络分几层,每层都是干什么的。

    1. [应用层] 支持网络应用,应用协议仅仅是网络引用的一个组成部分,运行在不同主机上的进程则使用应用层协议进行通信。主要的协议有:http,ftp,telnet,smtp,pop4
    2. [传输层] 负责为信源和信宿提供应用程序进程的数据传输服务,这一层上主要定义了两个传输协议,传输控制协议TCP,和用户数据报协议UDP【格式化信息流+提供可靠传输】
    3. [网络层] 负责将数据包独立地从信源发送到信宿,主要解决路由选择,拥塞控制和网络互连等问题
    4. [链路层] 负责将IP数据报封装成合适在物理网络上传输的帧格式并传输,或将物理网络接收到的帧解封,取出IP数据报交给网络层【将数据流发给网络层】
    5. [物理层] 负责将比特流在节点间传输,即负责物理传输。该层的协议即与链路层有关也与传输介质有关【其实就是通信设备相关】
  2. 一个请求从客户端发送到服务器,都经过什么过程

    1. 域名解析
    2. 发起TCP的3次握手
    3. 建立TCP连接后发起http请求
    4. 服务器响应http请求,客户端得到响应数据
    5. 客户端对数据进行解析
  3. HTTP请求方法都有哪些

    1. GET,HEAR,POST,PUT,DELETE,OPTIONS,TRACE
  4. 事件响应链相关

    1. hit-test responder
    2. touch如何传递到view和gesture recognizer
      1. 通过hit-test判断哪个view被触摸,这一层会考虑到(userInteractionEnable,hidden,alpha)
      2. 当touch状态发生改变的时候,应用调用自己的sendEvent,进而调用window的sendEvent:,windown通过掉用下面的方法来掉用合适的touch方法
        1. 当touch第一次出现时,会考虑mutipletouchEnable和exclusiveTouch,如果满足条件,则
          1. touch被传递给对应的view的gesture recognizer
          2. 该touch被传递给对应的view
        2. 如果一个gesture被检测出来,对于和这个gesture recognizer有关的touch
          1. touchesCacelled:forEvent: 会被掉用,touch不再传递给对应的view
          2. 如果这个touch还跟其他gesture recognizer有管理,其他的都直接设置为fail
        3. 如果一个gesture recognizer失败了,那么touch不会再传递给它,但是它们还会传递给对应的view
        4. 如果一个touch将被传递给一个view,但是这个view没有合适的处理touch的方法,responder会顺着responder chain 找到一个合适的并传递到那里
  5. UI相关

    1. 转场动画 1.
  6. 数据结构相关:链表,线性表,二叉树结构

    1. [链表]:是一种线性表,但不是按线性的顺序存储数据,而是在每个节点里存了下一个节点的指针。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另外一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而顺序表的时间复杂度分别是O(logn)和O(1)
    2. [线性表]:由N个数据元素组成的优先序列
    3. [二叉树]:就是每个节点最多有两个子树的树结构【二叉树常被用于实现二叉查找树和二元堆积】
  7. 常用三方库的实现 1.

  8. runloop,gcd,block,runtime

    1. [runloop]

      1. runloop 其实就是个事件循环,能够让线程能够随时处理事件而不退出
      2. 每个线程都会有一个runloop,但是runloop。但是如果我们不去主动获取runloop,那它就一直不会有(获取runloop:CFRunloopGetMain,CFRunloopGetCurrent)
      3. runloop的创建是发生在第一次获取时,销毁发生在线程结束时
      4. 我们开发中主要用到的是Runloop的Mode
        1. 对外暴露的Mode有:RunloopDefaultMode,RunloopCommonMode
        2. RunloopDefaultMode:app平时所处的状态
        3. TrackingRunloopMode:ScrollView滑动时候的状态的Model
        4. UIInitializationRunLoopMode:刚启动APP时进入的第一个Mode,启动完成后将不再使用
        5. GSEventReceiveRunLoopMode:接受系统时间的内部Mode,通常用不到
        6. CommonMode:组合,所有RunloopMode的组合,加入这个Mode,则会向一个给每个Mode都加
        7. 常见问题就是:我们给time加入了defaultMode,所以,tableView滑动的时候,它不计时
        8. 可用于:监控runloop的状态,然后在他处于defaultMode的时候,做些异步的事情
      5. Runloop的内部逻辑
    2. [gcd]

      1. GCD主要是和Block配合使用,进行多线程编程【GCD使用一套C接口,调用的系统底层的方法实现多线程的】
      2. 常用方法:
        1. dispatch_async,dispatch_sync,dispatch_group_async,dispatch_group_notify,dispatch_barrier_async,dispatch_apply,dispatch_after,dispatch_once,dispatch_wait[以及请求依赖]
        2. 需要注意的dispatch_sync【这个方法,是同步进行,所以,会等待当前线程的方法运行完毕后,再执行。如果我们在当前线程+执行sync方法,则会阻塞线程】
        3. 串行+并行 同步+异步
        4. 全局队列,不同的优先级别:background,low,default,high
        5. dispatch_barrier_async:可用于读写分离,在写的时候,执行dispatch_barrier_async,可保证之前的都读完了,才执行写的操作。
    3. [block]

      1. 使用的时候需要注意:循环引用
      2. __block 关键词:使用block后,它会将修饰的变量的指针地址,也放入block结构中,然后当在block中对其进行修改的时候,就可以正确响应到相关对象
      3. 三种block:全局block,堆block,栈block
      4. 全局block:没有引用任何局部变量的,其实就是全局block
      5. 因为ARC的开启,现在基本都是堆block。因为我们有默认的__strong修饰符。
      6. 栈block,其实就是引用了栈上的变量。。
    4. [runtime]

      1. 需要理解objc_msgSend(id,sel)【可参考:方法调用流程】
      2. Objective-C Runtime 运行时之三:方法与消息
  9. 常用sql语句

  10. 常用设计模式KVO,MVC,MVVM

  11. socket[传输层上面封装的套接字接口,方便应用层的使用】

    1. 定义:socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信
    2. socket 通信流程
  12. tpc,udp的区别

    1. TCP(传输控制协议) 面向连接,UDP(用户数据报协议)面向非连接
    2. TCP是可靠的,UDP不可靠
    3. TCP传输大量的数据,UDP少量数据
    4. TPC速度慢,UDP速度快
    5. 为什么说TCP可靠,UDP不可靠
      1. TCP,TCP必须要经过三次会话才能建立连接,并且会要求计算机成功接受到数据的时候,发送一个确认(ACK),如果未在某个时限内收到相应的ACK,则会重新发送数据包。【如果网络阻塞,这种重新发送会导致数据包重复,但是,接收计算机可使用数据包的序号来确定它是否为重复数据包,并在必要时丢弃它】
      2. UDP,UDP不必与对方先建立连接,不管对方是什么状态,就直接发送【类似于手机短信】
  13. 调用方法的过程

    1. 将代码转化为objc_msgSend
    2. msgSend干了什么事
      1. 首先绑定对象+SEL

      2. 会从对象所属的类找到相应的方法实现

        1. 如何找到方法的实现
          1. 在对象所属的这个类方法分发表中找到方法的selector,如果没有找到selector,则根据指向父类的指针找到其父类,并在父类的分发表中找到方法的selector,直到找到NSObject类【如果是类方法,则根据isa,从元类中找】
          2. 找到selector,函数就会找到具体的实现入口IMP,然后传入相应的参数,来执行方法的具体实现
          3. 如果没有找到selector,则会走消息转发流程
      3. 调用方法实现,并将接受者对象及方法的所有参数传给它

      4. 最后,它将实现返回的值作为它的自己的返回值

  14. 三次握手

    1. 客户端向服务器发起SYN包(同步序列编号),客户端进入SYN_SEND状态,等待服务器确认
    2. 服务器接收到客户端的SYN包,对其进行确认ACK,并向客户端发送SYN包,即:SYN+ACK,此时服务器进入SYN_RECV状态
    3. 客户端收到服务器的SYN+ACK包,并向服务器发送确认包ACK,此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手
  15. 什么是isa

    1. isa指向的是元类,元类其实就是类对象的objc_class结构体,里面存储了相关的类方法
    2. 元类的isa,最终指向的是自己,形成闭环
  16. IMP,Selector,Method

    1. IMP:方法的实现地址【指针】
    2. Selector:方法选择器
    3. Method:保存了Selector+IMP
  17. Load方法

    1. 调用顺序:父类的load——子类的load——分类的load 【注意,我们不需要使用[super load],系统自己会调用】
  18. initialize方法

    1. 只有我们显示的调用了[super initialize],父类的方法才会调用
    2. 每个类的initialize只会调用一次,分类中如果也有实现,是不会被再次调用的
  19. Copy

    1. 要支持copy操作,需要实现NSCoping协议的copyWithZone方法
    2. copy是不可变的拷贝操作,mutableCopy是可变操作
  20. NSCoding[归档操作]

    1. 需要实现NSCoding协议,initWithCoder encodeWithCoder
  21. 讲讲你对Hybrid App的理解,他与Native App之间的优缺点。

    1. [Hybrid App]
      1. 使用PhoneGap这类的中间件,以webView为用户界面,用javascript作为基本逻辑,再通过中间件访问底层API,进行app开发,比较依赖于webView的性能
      2. 使用Xamarin这种非官方语言工具,然后转换打包成原生应用
      3. 在原生应用的基础上,嵌入WebView,但整体框架还是原生应用提供
      4. 特性:跨平台(热修复),对于熟悉web的人员来说,相对开发成本低
    2. [Native App]
      1. 原生语言开发
      2. 特性:性能,交互较好,但不支持跨平台,不方便进行热修复
    3. [React Native]
      1. 使用JSX来生成HTML或者React标签
      2. UI生成:OC加载JS,然后进行解析映射,最后生成UI
      3. 事件:然后通过javascriptcore与OC进行交互调用
  22. iOS 代码混淆

    1. [方法名混淆]
      1. 外部创建一个sqliteDB,里面放入一些我们需要混淆的名字+要混淆成什么样子【或者是,我们随机生成混淆,然后DB中放入我们混淆后的方法名,方便我们对比查找】
      2. Xcode设置,build的时候,运行 .sh 脚本
        1. sh脚本:1.查询DB文件,找出需要混淆的内容
        2. 将这些内容使用#define的形式写入一个.h文件【实现文本替换】
      3. 在.pch文件中引用这个.h文件。
      4. 这样之后有人反编译我们的app,看到的就是我们混淆后的代码了
  23. KVC实现原理

    1. [user setValue:@"zhangsan" forKey:@"name"]
    2. 上面代码被转为
    	SEL sel = sel_get_uid ("setValue:forKey:");  
    	IMP method = objc_msg_lookup (site->isa,sel);  
    	method(site, sel, @"sitename", @"name"); 
    1. 就是先先找到setValue:forKey的sel,然后结合isa,找到具体的类+方法实现的接口,然后进行调用
  24. KVO的实现原理

    1. 这块实现原理有部分类似于KVC,当我们[addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil]的时候,应该是先获取到实例的isa,然后在获取到要设置的属性,然后在执行下面的重新指向子类的操作
    2. 当我们将实例的某个属性注册到观察者中的时候
    3. 系统会生成一个当前实例所属类的子类
    4. 在子类中重写set方法,在其中加入一些KVO用到的方法
    5. 将当前实例指向新创建的子类
    6. 然后,当我们调用set方法的时候,就会触发KVO
  25. OC中main.m方法解释

    int main(int argc, char * argv[]) {
    	@autoreleasepool {
            return UIApplicationMain(argc, argv, nil,NSStringFromClass([AppDelegate class]));
    	}
    }
    1. [@autoreleasepool]:将整个app放入一个autoreleasepool中,当app关掉后,释放所有内存
    2. [UIApplicationMain(argc, argv, nil,NSStringFromClass([AppDelegate class]))]:第三个参数会初始化一个UIApplication对象,用来接收事件(默认nil,会使用默认的UIApplication)
  26. 我们可以指定自己的UIApplication,例如下面[当接收到系统事件的时候,就会打印相应的Log]:

    class MyApplication: UIApplication {
    
 		-(void)sendEvent(event: UIEvent!) {
         	[super sendEvent:event];
         	Log("Event sent: %@",event);
     	}
 	}
 	
 	int main(int argc, char * argv[]) {
     	@autoreleasepool {
             return UIApplicationMain(argc, argv,NSStringFromClass([MyApplication class]),NSStringFromClass([AppDelegate class]));
     	}
 	}
  1. [UIApplicationMain(argc, argv, nil,NSStringFromClass([AppDelegate class]))]:第四个参数是指定由哪个类实现应用的委托AppDelegate【其实就是这些方法: didFinishLaunching 或者 didEnterBackground】

  2. 编译流程

    1. 确定标准库+头文件的位置
    2. 确定依赖关系(确定编译顺序,例如A依赖B,那么,就需要先编译B)
    3. 头文件的预编译【但不会编译#define】,保证只编译一次,而不是每次都重新编译
    4. 预处理【替换源码中的宏文件】
    5. 编译【将源码转为汇编,再有汇编转为机器码】
    6. 链接【将外部函数添加到可执行文件,例如静态库】
    7. 安装【将可执行文件保存到用户指定的目录】
    8. 操作系统连接【要求让操作系统记录程序的元数据】
    9. 生成安装包【将之前的可执行文件,打包为可执行的安装包】
    10. 动态链接【在运行期间,加载需要的链接,例如dylib,dll】
  3. UIView与CALayer

    1. 所有的Viw,都是有一个底层的layer来驱动的,由Layer提供绘制+现实
    2. VIew的尺寸样式也是Layer提供的。
    3. View跟Layer都有树状层级结构,有subLayers,subView。但layer比View多了个AnchorPoint属性(旋转的时候会用到)
    4. View是Layer的CALayerDelegate,View的现实内容有内部的CALayer的display
    5. CALayer,修改属性,支持隐式动画(属性有标记animatable的),再给UIView的layer做动画的时候,View作为layer的代理,View通过调用actionForLayer:forKey: 方法,让Layer做相应的动画action(动画行为)
    6. CALayer内部维护了3个tree
      1. presentlayer tree(动画树):做动画的时候,其实是presentlayer的属性值[所以,做完动画后,View又会回到原位]
      2. modeLayer tree(模型树):最终显示在界面上的,其实就是这个
      3. Render tree (渲染树)
    7. View可以处理事件,Layer不可以
    8. 修改View的属性,是不会有隐式动画的,但是Layer有,因为View默认禁止了layer的动画,在animation block中又将其启用
  4. scrollView 与全屏返回的手势有冲突怎么办?

    1. 需要给scrollView加个分类,然后实现UIGestureRecognizerDelegate
    2. 实现这个方法:- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
    3. 这个方法指明:如果有多个手势同时触发,是否同时识别另外一个手势
    4. 如果返回YES,则会同时识别两个手势
    5. 如果返回NO,则只识别当前手势
  5. swift

    1. 相关内容:https://nicolastinkl.gitbooks.io/maker/content/swift_tips.html
    2. swift3 柯里化被移除了,不再支持[柯里化:把接受多个参数的方法变换成接受 第一个参数的方法,并且返回接受余下的参数并且返回结果的新方法.]
    3. swift中并没有main.m文件了,而是在APPDelegate文件中,有这么一句:[@UIApplicationMain]
      1. 这其实就是将被标注的类作为委托,去创建一个 UIApplication 并启动整个程序。在编译的时候,编译器将寻找这个标记的类,并自动 插入像 main 函数这样的模板代码
      2. 如果你不想要这个标签,也可以自己新建个main.swift文件,来做OC中main文件所做的事情
    4. swift为了追求性能,一般情况下,是不会做些运行时的事情。swift的类型,成员变量,方法是在编译的时候就确定的,不需要再经过一次查找就可以直接使用
    5. [@objc] 关键词
      1. 如果我们要使用objc的方法或特性,来调用纯swift的类型的时候,就会提示找不到所需要的运行时信息。
      2. 这个时候,就需要我们手动加上@objc关键字
      3. 或者将swift的类,继承自NSObject,这样,就会自动将不是private的成员,添加@objc关键字
  6. 相关文章

    1. Foundation: NSObject Class
    2. Objective-C Runtime 运行时之三:方法与消息
    3. Objective-C Runtime 运行时之一:类与对象
    4. GCD 深入理解:第一部分
    5. GCD 深入理解:第二部分