IOS知识体系

文章目录

    • 一、UI视图
      • 其中包括事件传递、视图响应、UI布局、绘制、Tableview重用机制的理解等基本技术点,也包括如离屏渲染、流式页面的性能优化、异步绘制、UI渲染机制等偏底层技术的考察。
    • 二、Objective-C语言
      • 其中包括如KVO、KVC、分类、扩展、关联对象等系统实现原理和机制,以及NSNotification、属性关键字等相关技术点的考察。
    • 三、Runtime
      • 可以说是中级以及以上工程师的必备技术要求,涉及大家对对象、类对象、原类对象的理解、消息传递机制、消息转发流程、Method-Swizzling、ISA-swizzling、动态方法解析、动态添加方法等。
    • 四、内存管理
      • 内容基本包括:weak自动置nil、ARC、MRC、自动释放池的实现原理、循环引用、引用计数管理思想等。
    • 五、Block
      • iOS当中非常重要的OC语言特性。其中包括截获变量特性、__Block关键字、Block的本质、Block的内存管理和循环引用等。
    • 六、多线程
    • iOS常见的多线程技术NSOperation&NSOperationQueue 、NSThread、以及快用烂了的GCD;那么面试过程当中,往往会结合实际代码考察同学们对多线程技术的掌握深度,包括对于常见锁的考察,如NSLock、递归锁、自旋锁、条件锁等等。
    • 七、RunLoop
      • 相信大家知道RunLoop可以有事做事,没事休息?可能要更深入些,RunLoop为什么会有事做事没事休息,系统是怎样实现的。哈哈,是不是有难度了,再比如怎样实现一个常驻线程、RunLoop和线程的关系是怎样的等等。
    • 八、网络
      • 其中包括HTTP相关的中间人攻击、HTTPS的连接建立流程、对称加密、非对称加密、DNS劫持、TCP的滑动窗口协议、可靠传输是怎样保证的,以及TCP的慢启动特点,Session/Cookie的区别等等,这些都是面试中高级岗位必考问题。
    • 九、设计模式
      • 其中包括常见的软件设计原则,责任链、适配器、桥接、命令、单例、策略模式等等,不要告诉我你只是看了几本书,面试官会让你结合实际业务场景,现场考察你对设计模式的运用和理解的。
    • 十、架构/框架
      • 其中包括常见的如怎样设计图片缓存框架、网络框架,客户端的整体架构怎样实现,常见的解耦方式有哪些,多数同学都知道OPENURL是一种解耦方案,那依赖注入这种方式可能iOS的同学会感到陌生,这也是面试官期许的答案。
    • 十一、算法
    • 有序数组归并、链表反转、字符串反转、大数相加算法思想等等,这部分变化就很多了。
    • 十二、第三方
      • 常见的AFNetworking、SDWebImageView、Reactive Cocoa、React Native等等

一、UI视图

其中包括事件传递、视图响应、UI布局、绘制、Tableview重用机制的理解等基本技术点,也包括如离屏渲染、流式页面的性能优化、异步绘制、UI渲染机制等偏底层技术的考察。

  1. UI卡顿/掉帧的原因: 知识背景: 1)页面滑动的流畅性是60fps,即1s有60帧的画面更新才可以让人眼感到画面流畅。
    在规定的时间内,即16.7ms内(按照1s完成60帧来计算),由CPU+GPU共同协同产生一帧的数据。
    CPU在做UI布局,文本计算,图片解码以及绘制等工作占用时间过长,导致留给GPU的时间就非常少,GPU要想把图层合成和纹理渲染准备完毕,所需的总时间就可能会超过16.7ms,这样在当下这一帧VSync信号到来之前,我们没有准备好当下这一帧的画面,就产生了掉帧现象,我们肉眼看到的效果就是滑动的卡顿现象。

  2. UI视图总结:可以从CPU+GPU两方面来说明,对于CPU来说,可以采用在子线程中创建对象、调整和销毁;包括进行与排版以及图片的解码,以及采用图片的异步绘制。

  3. 避免离屏渲染: 原因: 离屏渲染是发生在GPU层面,使得GPU触发了OpenGL的多通道渲染管线,产生了额外的开销。 所以
    1)创建新的渲染缓冲区
    增加了内存的开销,包括上下文切换,因为有多通道渲染管线,需要把多通道的渲染结果做一个最终的合成,就需要上下文切换,这就造成了GPU额外的开销。
    2)离屏渲染增加了GPU的工作量,使得CPU+GPU的工作时间超出了16.7ms的总耗时,可能会导致UI的卡顿和掉帧。

  4. 异步绘制原理图: 通过子线程的切换,借助Global queue,在子线程中进行位图的绘制,此时主线程可以做其它的工作。等子线程绘制位图完毕,再回到主队列中提交位图,设置给CALayer的contents属性,完成一个UI控件的异步绘图过程。

  5. UIView的绘制原理:
    1)调用setNeedsDislay时候,(实际上是这个view的layer调用setNeedsDisplay方法,之后相当于在这个layer上打上一个脏标记),然后并没有立即发生当前视图的绘制工作,而是在当前runloop快要结束的时候调用CALayer的display方法,进入到当前视图真正的绘制工作的流程当中。
    原因是由于要减少绘制次数,

  6. 提升性能,所以要在当前runloop快要结束的时候调用CALayer的display方法。

  7. 滑动优化方案之二: 1.纹理渲染 1)避免离屏渲染 2)依托于CPU的异步绘制机制来减轻GPU的压力 2视图混合
    1)如果多个view层层叠加,GPU就要做每个视图的合成,合成每一个像素点的像素值,需要进行大量的计算,
    如果减轻视图的复杂性,就可以减轻GPU的压力;
    也可以采用CPU的异步绘制,使得提交的位图本身就是一个层级很少的视图,也可以减轻GPU的压力

  8. 事件传递是由上而下(application->window ->
    view遍历子view(反序),通过hitTest和pointInside找到最合适的view)
    2.事件响应是自下而上(视图的响应链),意思是谁来响应/处理这个事件。touchBegan等,到最上层如果uiapplication都不响应处理,就舍弃.而不是崩溃

事件传递流程

二、Objective-C语言

其中包括如KVO、KVC、分类、扩展、关联对象等系统实现原理和机制,以及NSNotification、属性关键字等相关技术点的考察。

  1. KVO具体实现机制和原理: 1)发生在运行时。
    当注册类A的一个对象的观察者时,实际上是调用了ObserverForPath这个方法,系统会在运行时动态创建一个NSKVONotifying_A的一个类,将原来A中的isa指针指向KVONotifying_A,这也就是isa混写技术的一个标志。
    KVONotifying_A是原来类A的一个子类,之所以进行这样的继承关系是为了重写原来类A中的setter方法,然后通过重写setter达到通知所有观察对象的目的。使用setter方法改变值,或setValue:forKey:改变值,kvo会生效,成员变量直接修改需要手动添加kvo才会生效

  2. valueForKey的实现流程: 1)访问器方法(Accessor Method )是否存在的判断规则:
    2)实例变量(Instance var)是否存在的判断规则

  3. 手动KVO的实现: 在对变量直接成员赋值的时候,在赋值之前添加
    willChangeValueForKey,在之后添加didChangeValueForKey,即可。

  1. assign的特点: 悬垂指针:
    在被assign修饰的对象,在被释放之后,assign仍然指向原对象地址。如果此时继续访问原对象,可能会导致内存泄露或者程序异常。
  2. 深拷贝: 1)不会增加被拷贝对象的引用计数; 2)产生了新的内存分配,出现了两块内容相同的内存;

  1. weak的特点: 多用来解决循环引用问题; 可以修饰基本数据类型,也可以修饰对象;
  2. 属性关键字之原子性: atomic修饰的变量可以保证获取和赋值操作是线程安全的。这个获取和赋值并不代表操作(添加或者删除)和访问。

三、Runtime

可以说是中级以及以上工程师的必备技术要求,涉及大家对对象、类对象、原类对象的理解、消息传递机制、消息转发流程、Method-Swizzling、ISA-swizzling、动态方法解析、动态添加方法等。

  1. isa的指向: 1)关于对象,也就是实例,在runtime中对应于objc_object,它其中有一个isa指针,指向对应的类。
    2)关于类对象,Class代表objc_Class,继承objc_object,自然也是有isa指针,指向其元类对象MetaClass。
    所以,在进行方法调用的时候,实例会通过isa指针就会去类中进行查找;类对象则会通过isa指针到它的元类方法中进行方法查找。
  2. isa指针: 是一个共用体类型。 一般是32位或者64位。 分为两种: 1)指针型isa:代表Class的地址。
    2)非指针型isa:比如64位其中的30+、40+位代表Class地址,就可以寻找到所有Class,就够用了。其余位则表示其它相关内容达到节省内存的目的。这也是有两种isa的初衷。
  3. cache_t: 局部性原理: 在调用方法的时候,往往调用的都是调用频次最高的,这时把这些方法放在缓存中,下次命中的概率会高一些。
    cache_t是一个数组,每个元素都是一个bucket_t,其中包含两个成员变量,一个是key,即selector;另外一个IMP是一个无类型的函数指针。
    比如给出一个key,就可以通过哈希查找算法定位key对应的这个key位于数组当中的位置,然后通过提取IMP来调用函数。
  4. class_data_bits_t: 是objc_class中的结构。
    class_rw_t代表了类相关的读写信息,比如关于给类添加的一些分类方法、属性、协议,也对class_ro_t进行了封装。
    class_ro_t代表了类相关的只读信息 class_ro_t:
    methodList是一维数组(由于该结构是只读的,不允许添加新元素),所以存放的是原有的一些方法。


实例方法的消息转发流程,第2次机会: 2)如果第一次转发返回NO系统给第二次机会来处理,会回调(id )forwardingTargetForSelector:,返回值是个id类型的对象,相当于告诉系统这个选择器或者实例方法的调用,应该由哪个对象处理,转发对象是谁,如果指定了一个转发目标,系统会把这条消息转发给返回的转发目标并结束当前的转发流程

消息传递的编译层面发生了什么? objc_msgSend(消息传递的接受者,选择器或者消息名称);
实例对象、类对象、元类对象关系示意图: 注意: 1)对于任何一个元类对象的isa指针都会指向根元类对象rootClass。即superClass或者subClass,包括根元类对象rootClass自己。 2)类对象和元类对象都是objc_class这种数据结构,也是继承于objc_object,所以都有isa指针。
缓存查找的具体步骤: 利用哈希算法查找


实例对象、类对象、元类对象关系示意图: 注意: 1)对于任何一个元类对象的isa指针都会指向根元类对象rootClass。即superClass或者subClass,包括根元类对象rootClass自己。 2)类对象和元类对象都是objc_class这种数据结构,也是继承于objc_object,所以都有isa指针。


IOS知识体系_第1张图片

四、内存管理

内容基本包括:weak自动置nil、ARC、MRC、自动释放池的实现原理、循环引用、引用计数管理思想等。

内存布局
IOS知识体系_第2张图片

  1. 内存管理方案概述: 1)TaggedPointer,对于一些小对象,如NSNumber类;
    2)NONPOINTER_ISA,非指针型的isa,对于64位架构下iOS应用程序采用NONPOINTER_ISA这种内存管理方案;在64架构下,这种isa指针占据32位或者40位比特位,剩下的比特位存储内存管理的数据内容。
    3)散列表包括引用计数表和弱引用计数表。
  2. 散列表方式实现内存管理涉及到的数据结构: 1)Spinlock_t 自旋锁 2)RefcountMap 引用计数表
    3)weak_table_t 弱引用表
  3. NONPOINTER_ISA:
    arm64架构下,一共有64个比特位,分为两种:一种只代表当前类对象的地址;另外一种不仅存储类对象的地址,还有一些内存管理的数据:
    0~15位:
    第0位代表indexed,是一个标识位,0代表isa指针只是一个纯的isa指针,只代表当前类对象的地址;1代表不仅存储类对象的地址,还有一些内存管理的数据。
    第1位代表has_assoc, 表示当前对象是否有关联对象:0代表没有,1代表有。
    第2位代表has_cxx_dtor,表示当前对象是否使用到C++的部分。 第3~31位代表shitrcls,表示当前对象的类对象的地址。
  4. NONPOINTER_ISA: 第32~47位: 第32~35位表示shiftcls,表示当前类对象的地址;
    第36~41表示magic。 第42位表示weakly_referenced,标识当前对象是否有weak指针;
    第43位表示deallocating,标识当前对象是否正在进行dealloc操作。
    第44位表示has_sidetable_rc,也就是散列表,标识当前isa指针中,如果存储的引用计数超过上限,需要外挂一个sidetable去存储引用计数内容;
    第45~63位表示extra_rc,当引用计数是一个很小的值的范围之内的时候就会存储在这里。
  5. 当一个对象被释放之后,weak变量是如何处理的? 当一个对象被dealloc之后,在dealloc()
    的内部实现当中会去调用弱引用清除的相关函数,然后在weak_clear_no_lock()当中,会根据当前对象指针查找弱引用表,把当前对象相对应的弱引用都取出来得到一个数组,然后遍历这个数组当中的弱引用指针,分别置为nil
  6. 自动释放池:以栈为节点通过双向链表的形式组合而成,和线程一一对应
  7. AutoreleasePoolPage::push操作的具体实现:
    如果有元素进行push操作,next位置会置为nil,实际上是称它为哨兵对象;然后把next指针指向下一个可入栈的位置。
    所以每次在进行一个AutoreleasePool代码块的创建时,就是在栈中插入一个哨兵对象,哨兵对象用来区分不同AutoreleasePool。
  8. 手动创建autoreleasepool的场景:
    在for循环中alloc图片数据等内存消耗过大的场景要手动插入autoreleasepool:
    每次for循环都进行一次release,要降低内存的峰值,防止内存消耗过大导致的问题。
  9. AutoreleasePool为何可以多层嵌套? 多层嵌套就是多次插入哨兵对象。
    AutoReleasePool的嵌套实际上就是AutoReleasePoolPage::Push调用 ,这个调用实质上就是插入一个哨兵
    ,而是否增加链表节点 取决于当下的Page是否是满的。

五、Block

iOS当中非常重要的OC语言特性。其中包括截获变量特性、__Block关键字、Block的本质、Block的内存管理和循环引用等。

  1. __block说明符在MRC和ARC下的区别: __block说明符的作用是 修饰变量后 可以让变量成为对象,MRC下,__block修饰的变量成为对象后,被block使用后没有强引用的关系,而ARC下有block对__block修饰的变量有强引用的关系
  2. 什么是Block? Block是函数及其上下文封装的对象
  3. 为什么Block会产生循环引用:
    1)如果当前Block对当前对象的某一个成员变量进行截获,这个Block会对这个变量有一个强引用,而当前对象对Block也有一个强引用,就产生了自循环引用。可以声明该变量为__weak类型来进行循环引用的破除。
    2)如果定义了__block说明符的变量var,var经过clang编译变成一个对象A,在block中会对A产生强引用,从而产生循环引用。但是可以在block中设置A
    = nil来断环避免循环引用。弊端是如果不调用该block,该循环引用就一直存在;在MRC下不会在block中对A强引用所以不会产生循环引用
  4. 怎样理解Block截获变量的特性: 应该从变量的类型来解答:
    1)如果变量为基本类型的局部变量,则只是截获其值,不会修改外部变量;如果类型为static的局部变量,则截获的是其指针,可以修改外部变量;对于全局变量,静态全局变量不产生截获;如果截获的是对象,则会连同其所有权修饰符引起截获。
  5. 栈上用__Block修饰符修饰的变量在copy之后,在堆上分配了一个与原来变量一样的内存,并且栈上变量的forwarding指针指向堆上block变量的地址;堆上变量forwarding指针也指向自己block变量的地址;
    也就是说,在__Block变量进行copy之后会在堆上产生一个相同的变量,forwarding指针都指向堆上__Block变量。
  6. __forwarding存在的意义: 如果对__block 变量不copy,操作的就是栈上的__block 变量; 如果发生了copy,无论操作的是栈上、还是堆上__block 变量,都是使用的堆上__block 变量。
    在MRC环境下,对Block进行copy之后,是否会引起内存泄漏?是的。
    随着栈上block和__Block变量作用域的结束,该block和__Block变量会销毁,但是堆上的block或者__Block变量没有其它成员变量去指向它,引起内存泄漏。
  7. 对截获的静态局部变量,全局变量,静态全局变量进行赋值时,不需要__block修饰符:
    因为对于静态局部变量,实际操作的是外部的静态局部变量; 对于全局变量,静态全局变量不涉及截获操作;
  8. 对截获的局部变量,包括基本数据类型和对象类型,进行赋值时,需要在变量的声明处添加__block修饰符
  9. NSTimer的循环引用示例讲解2: 2)无法通过S对T改为弱持有来解除相互循环引用:
    因为NSTimer当被分派之后,会被当前线程的runloop强持有,如果S是在主线程中创建,T就会由主线程的runloop强持有,所以即使S弱引用T,由于主线程的runloop常驻内存,并对T强引用,再通过T对S强引用,所以即使S弱引用T,也还是T对S产生了强引用。此时即使VC页面退出,而主线程强引用S,也不会释放S,造成S的内存泄漏。
  10. NSTimer的循环引用示例讲解4:
    2)如果T为重复定时器,可以在NSTimer的T和S之间添加一个中间对象M,其中由T对M强引用,M对T、M对S弱引用。当前页面或者VC退出之后,VC释放了S的强引用,下次定时器的回调事件回来时,可以在中间对象M中,T的回调方法里进行判断当前M所持有的弱引用S是否被释放,也就是判断M中所持有的weak变量S是否是nil,恰好利用了一个weak对象被释放后,指向它的指针会被设为nil的特性。然后将M中对T的回调方法中调用invalid,并置为nil。
    这样就可以实现runloop对T强引用,T对M的强引用,实现T和M的释放,达到破除循环引用的效果。

六、多线程

iOS常见的多线程技术NSOperation&NSOperationQueue 、NSThread、以及快用烂了的GCD;那么面试过程当中,往往会结合实际代码考察同学们对多线程技术的掌握深度,包括对于常见锁的考察,如NSLock、递归锁、自旋锁、条件锁等等。

  1. @synchronized 一般在创建单例对象的时候用 atomic 修饰属性的关键字 对被修饰对象进行原子操作(不负责使用)
  2. OSSpinLock: 自旋锁定义: 循环等待询问,不释放当前资源。 应用场景: 用于轻量级数据访问,简单的int值 +1/-1操作。
  3. NSLock: 会产生死锁。
    由于某个线程的methodA中已经调用了lock方法,加锁之后获取了这把锁,然后又调用了methodB,methodB中对同一把锁也调用了lock方法,这相当于已经获取了这把锁又再次获取这把锁,由于这种重入的原因导致了死锁。
  4. NSLock解决办法: 通过NSRecursiveLock递归锁解决。 NSRecursiveLock递归锁的特性是可重入。
    把methodA和methodB中的锁换成NSRecursiveLock递归锁即可。
  5. 自旋锁: Spinlock_t:
    是“忙等”的锁,如果该锁已被其它线程获取,那么当前线程就会不断探测这个锁是否被释放,如果被释放则第一时间获取该锁;
    其它的锁,比如信号量,当线程获取不到锁的时候,会阻塞等待;待其它线程释放锁了之后,唤醒该线程获取锁;
  6. NSOperation对象在Finished之后是怎样从queue当中移除的?NSOperation对象在Finished之后,会在内部通过KVO的方式通知内部的NSOperationQueue移除。
  7. iOS系统为我们提供了几种多线程技术以及各自特点是怎样的? 回答: 主要提供了三种多线程技术:
    分别为GCD,NSOperation和NSOperationQueue,NSThread。
    1)使用GCD来实现一些简单的线程同步,包括一些子线程的分派,实现多读单写这种场景问题的解决。
    2)NSOperation用来实现一些第三方框架,如AFN,SDWebimaging,其特点可以方便我们对任务状态的控制,添加依赖、移除依赖。
    3) NSThread用来实现一个常驻线程。
  8. NSOperation: 优势和特点: 1)添加任务依赖 2)任务执行状态控制 3)最大并发量
  9. 系统是怎样移除一个isFinished=YES的NSOperation的? 回答:通过KVO。
  10. dispatch_barrier_async的作用: 可以用GCD实现多读单写。
  11. 多读单写方案: 需要使用异步栅栏调用,分派到并发队列中。
    dispatch_barrier_async(concurrent_queue,^{//写操作})
  12. viewDidLoad内调用同步主队列串行产生死锁: 原因是队列引起的循环等待;
    由于GCD中使用的是主队列,都要是在主线程中处理的,队列有先进先出的特性, 1)应该先运行viewDidLoad,再运行Block。
    2)由于在view方法中有block,只有先运行完Block才能结束view方法; 所以造成了循环等待。

七、RunLoop

相信大家知道RunLoop可以有事做事,没事休息?可能要更深入些,RunLoop为什么会有事做事没事休息,系统是怎样实现的。哈哈,是不是有难度了,再比如怎样实现一个常驻线程、RunLoop和线程的关系是怎样的等等。

  1. runloop是一个事件循环用于处理事件消息和对他们的管理的对象。
    2)在调用CFRunLoopRun方法中,会调用系统方法mach_msg,同时发生从用户态到内核态的切换,然后当前线程处于休眠状态,做到有事做事,没事休息。
  2. 用户态和内核态介绍: 我们的应用程序都是运行在用户态上的。
    用户进程以及开发中使用的绝大多数API都是在用户层面的,而发生的系统调用需要使用关于操作系统以及底层内核相关的指令和API就相当于触发了系统调用,有些系统调用就会发生状态空间的切换。
    这种切换空间是对计算机的一些资源调度、管理进行统一或者一致性的操作,避免特殊的异常,合理的安排资源调度;同时内核态的一些内容可以对用户态的线程进行调度、管理、进程间通信。
  3. 事件循环Event Loop:
    1)没有消息需要处理时,进程或者线程会进入休眠状态,而休眠状态的过渡相当于把当前线程的控制权转移给了内核态。
    2)有消息需要处理时,就会有一个从用户态到内核态的状态切换。
    3)维护的事件循环可以用来不断的处理消息或事件,对他们进行管理,如果没有消息进行处理,会从用户态切换到内核态,进行资源的休眠避免资源占用;当有消息进行处理时,会发生从内核态到用户态的切换,当前用户线程会被唤醒;
    状态的切换是回答该问题的关键点。
  4. runLoop与线程是怎样的关系? 1)两者一一对应的关系 2)一个线程默认是没有runloop,需要手动加上runloop。
  5. 如何实现一个常驻线程: 1)创建一个runoop。 2)给runloop添加source/timer/observer事件以及port。
    3)调用run方法。 注意: 运行的模式和资源添加的模式必须是同一个,否则可能由于外部使用while循环会导致死循环。
  6. 怎样保证子线程数据回来更新UI的时候不打断用户的滑动操作?
    1)用户滑动操作时runloop是运行在kCFRunLoopUITrackingMode下,网络请求一般放在子线程中,子线程返回给主线程的数据要抛回给主线程进行UI更新,把这部分的逻辑包装起来提交到主线程defaultMode下,这样进行mode隔离就避免了问题。
    RunLoop和NSTimer1: 滑动TableView的时候我们的定时器还会生效吗?
    当前线程正常请看下是运行在kCFRunLoopDefaultMode,当滑动tableview时,会发生mode的切换,切换到UITrackingRunLoopMode,此时定时器就不会再生效。
  7. RunLoop和NSTimer2: void CFRunLoopAddTimer(runLoop,timer,commonMode);
    把NSTimer添加到当前runloop的commonMode中。
    commonMode并不是实际存在的mode,它只是把一些mode打上commonMode的标记,把某个事件源同步到多个mode当中。
  8. 从屏幕上点击开始系统发生了什么?
    调用了main函数之后,会调用UIApplicationMain,在内部会启动主线程的runloop,进过一系列的处理runloop处于休眠状态。如果此时点击屏幕产生了mach-port,最终转成source1事件,把主线程唤醒,运行处理。当我们把程序杀死时,会触发kCFRunloopExit通知,即将退出runloop,线程被销毁。
  9. CFRunLoop的成员数据结构: pthread:与runloop一一对应,是当前runloop所在的线程。
    currentMode:CFRunLoopMode类型,当前runloop所处的mode;
    modes:NSMutableSEt类型,所有mode的集合。
    commonModes:NSMutableSet commonModeItems:
  10. CFRunLoopMode的成员数据结构: name:NSDefaultRunLoopMode
    sources0、sources1:MutableSet无序 observers、timers:MutableArray有序
  11. CFRunLoopObserver 1: 6个观测时间点。 1)kCFRunLoopEntry:
    runloop的入口时机,当runloop启动时,系统会给我们一个回调通知。 2)kCFRunLoopBeforeTimers:
    通知观察者,runloop将要对NSTimer一些相关事件进行处理。 3)kCFRunLoopBeforeSources
    将要处理一些source事件。
  12. CFRunLoopObserver 2: 6个观测时间点。 4)kCFRunLoopBeforeWaiting
    当前runloop将要进行休眠状态。即将要发生用户态到内核态的切换。 5)kCFRunLoopAfterWaiting
    发生的时机是从内核态切换到用户态之后。 6)kCFRunLoopExit runloop退出的通知。
  13. 各个数据之间的关系: runloop和model是一对多的关系。
    mode和source,timer,observer也是一对多的关系。
  14. RunLoop的Mode:
    当我们运行在mode1上时,只能接收处理mode1当中的source1,timers,observers.不能处理mode2、mode3里的。
  15. 同一个timer、observer、source事件可以添加到多个mode上,这样保证mode切换的时候正常处理接收事件。需要借助CommonMode。
  16. runLoop流程
    IOS知识体系_第3张图片

八、网络

其中包括HTTP相关的中间人攻击、HTTPS的连接建立流程、对称加密、非对称加密、DNS劫持、TCP的滑动窗口协议、可靠传输是怎样保证的,以及TCP的慢启动特点,Session/Cookie的区别等等,这些都是面试中高级岗位必考问题。

  1. HTTPS和HTTP有怎样的区别? HTTPS = HTTP+SSL/TLS
  2. GET和POST方式的区别 标准答案: 1)从语义的角度来回答 get是获取资源。安全的,幂等的,可缓存的。
    post是处理资源。非安全的,非幂等的,不可缓存的。
  3. session工作流程:
    1)客户端发送http请求报文,服务器端会进行2步,比如记录用户状态,密码和用户名,同时会生成sesssionid,再把sessionid用setCookie设置给http响应报文的头部发给客户端;
    2)客户端在后续的请求过程中,在http请求头部字段的cookie中,设置所接收到的sessionid,这样server就可以通过sessionid来识别用户。
  4. session和cookie的关系是怎样的? 1)session需要依赖于cookie机制。
  5. Session的定义: session是用来记录用户状态,区分用户的;状态存放在服务器端。
  6. 怎样保证cookie的安全? 1)对cookie进行加密处理 2)只在https上携带cookie
    3)设置cookie为httpOnly,防止跨站脚本攻击。
  7. 怎样删除cookie? 1)新cookie覆盖旧cookie;
    2)覆盖规则:name,path,domain等需要与原cookie一致。
    3)设置cookie的expires=过去的一个时间点,或者maxAge = 0,相当于说明这个cookie是无效的;
  8. 怎样修改cookie? 1)新cookie覆盖旧cookie,覆盖规则:name,path,domain等需要与原cookie一致。
  9. Cookie的定义: 主要用来记录用户状态,区分用户;状态保存在客户端。
    server通过http响应报文中的头部添加一个字段setCookie把对应内容通过首部字段传给客户端,客户端在后续的http请求报文中把server返回的cookie添加到http请求报文的cookie首部字段当中,就可以传回给server端,server端就可以判断cookie来对用户进行识别。
  10. Session/cookie
    是对http协议无状态(对于同一个用户,多次进行http请求时,server无法记住是否是同一个用户,比如淘宝里添加物品到购物车)特点的补偿。
  11. 怎样解决DNS劫持? 1)httpDNS 2)长连接
  12. httpDNS: DNS解析是通过DNS协议向DNS服务器的53端口进行请求;
    httpDNS是使用http协议向DNS服务器的80端口进行请求。 这样就不涉及DNS解析,自然不会被劫持。
  13. DNS劫持与HTTP的关系是怎样的? 1)没有关系。 2)因为DNS解析是发生在HTTP建立连接之前。
    3)DNS解析请求使用UDP数据报,端口号53
  14. DNS劫持图示:
    1)比如客户端去询问本地DNS服务器来查询某一域名所对应IP地址,由于DNS解析过程是基于UDP并且是明文传输,就可能涉及到窃听问题。
    2)假如有一个钓鱼DNS服务器,可能会劫持到客户端发给DNS服务器的请求,然后钓鱼DNS服务器可能会返回一个错误的IP地址;
    3)我们拿到错误的IP地址去进行访问时,访问的就是错误的网站。
  15. DNS解析: 1、定义: 域名到IP地址的映射,DNS解析请求采用UDP数据报,且明文。 2、解析过程:
    客户端在进行向server端发送网络请求时,需要先经历一个DNS的过程,也就是域名到地址映射的过程;
    客户端会通过DNS协议向DNS服务器请求对相应域名的解析,DNS服务器把解析结果的IP地址返回给客户端,再由客户端向对应的IPserver发送相应的网络请求。
  16. TCP的拥塞控制策略: 1)慢开始、拥塞避免 2)快恢复、快重传
  17. TCP面向字节流的概念: 发送方有一个缓冲区; 接收方也有一个缓冲区;
    无论发送方一次给TCP缓冲区多少字节,对于TCP本身来说,会根据实际情况进行划分一次性传递多少字节,并不是发送方一次性把所有字节流一次性发给接收方。
  18. 三次握手的意义: 为了应对网络中存在的延迟或者重复数据的问题。
    IOS知识体系_第4张图片
  19. 持久连接: 头部字段: connection:keep-alive,表示客户端期许采用持久连接。
    time:20,持久连接持续多久有效。 max:10,这条连接最多发生多少个http请求。

九、设计模式

其中包括常见的软件设计原则,责任链、适配器、桥接、命令、单例、策略模式等等,不要告诉我你只是看了几本书,面试官会让你结合实际业务场景,现场考察你对设计模式的运用和理解的。

IOS知识体系_第5张图片
六大设计原则

  1. 单一职责原则:一个类只负责一件事
  2. 依赖倒置原则:抽象不该依赖于具体实现,具体实现可以依赖抽象
  3. 开闭原则:对修改关闭,对扩展开放
  4. 里氏替换原则:父类可以被子类无缝替换,且原有功能不受影响(例如:KVO)
  5. 接口隔离原则:使用多个专门的协议、而不是一个庞大臃肿的协议(例如:UITableViewDelegate,UITableViewDataSource)
  6. 迪米特法则:一个对象应当对其他对象尽可能少的了解(高内聚、高耦合)

责任链模式
主要思想:对象引用了同一类型的另一个对象,形成一条链。链中的每个对象实现了相同的方法,处理对链中第一个对象发起的同一请求,如果一个对象不知道如何处理,就把请求传给下一个响应器。

@class BusinessObject;
typedef void(^CompletionBlock)(BOOL handled);
typedef void(^ResultBlock)(BusinessObject *handler, BOOL handled);
​
@interface BusinessObject : NSObject
​
// 下一个响应者(响应链构成的关键)
@property (nonatomic, strong) BusinessObject *nextBusiness;
// 响应者的处理方法
- (void)handle:(ResultBlock)result;
​
// 各个业务在该方法当中做实际业务处理
- (void)handleBusiness:(CompletionBlock)completion;
@end
@implementation BusinessObject
​
// 责任链入口方法
- (void)handle:(ResultBlock)result
{
  CompletionBlock completion = ^(BOOL handled){
      // 当前业务处理掉了,上抛结果
      if (handled) {
          result(self, handled);
      }
      else{
          // 沿着责任链,指派给下一个业务处理
          if (self.nextBusiness) {
              [self.nextBusiness handle:result];
          }
          else{
              // 没有业务处理, 上抛
              result(nil, NO);
          }
      }
  };

  // 当前业务进行处理
  [self handleBusiness:completion];
}
​
- (void)handleBusiness:(CompletionBlock)completion
{
  /*
    业务逻辑处理
    如网络请求、本地照片查询等
    */
}
​
@end

桥接模式
桥接模式的目的是把抽象层次结构从其实现中分离出来,使其能够独立变更。
IOS知识体系_第6张图片
Class A 和ClassB都是抽象类。ClassA中一个成员变量是ClassB的对象。ClassB中作为抽象类,只提供了默认的接口,并没有实现。B1、B2、B3是ClassB的三个子类,重写父类的接口方法,提供不同的实现,此时对于ClassB使用方来说,是感知到不使用了哪个实现。ClassA中有一个handle处理方法,默认调用成员变量ClassB对象中的接口方法。A1、A2、A3三个是ClassA的子类,对于子类来说可以覆写父类的handle方法,做一些自定义的操作。
** 代码示例: ClassA **

#import 
#import "BaseObjectB.h"
@interface BaseObjectA : NSObject
​
// 桥接模式的核心实现
@property (nonatomic, strong) BaseObjectB *objB;
​
// 获取数据
- (void)handle;
@end
#import "BaseObjectA.h"
​
@implementation BaseObjectA
​
/*
  组合方式:
  A1 --> B1、B2、B3         3种
  A2 --> B1、B2、B3         3种
  A3 --> B1、B2、B3         3种
*/
- (void)handle
{
  // override to subclass
  // 处理objB中的方法。
  [self.objB fetchData];
}
​
@end

** ClassA的子类A1、A2、A3重写父类中handle方法。**

#import "ObjectA1.h"
​
@implementation ObjectA1
​
- (void)handle
{
  // before 业务逻辑操作

  [super handle];

  // after 业务逻辑操作
}
@end

** ClassB 实现 **

#import 
​
@interface BaseObjectB : NSObject
​
- (void)fetchData;
​
@end
#import "BaseObjectB.h"
​
@implementation BaseObjectB
// 默认逻辑实现
- (void)fetchData
{
  // override to subclass
}
@end

** ClassB的子类进行具体的逻辑实现。**

#import "ObjectB1.h"
​
@implementation ObjectB1
​
- (void)fetchData{
  // 具体的逻辑处理
}
@end

** 使用方代码实现 **

@interface BridgeDemo()
@property (nonatomic, strong) BaseObjectA *objA;
@end
​
@implementation BridgeDemo
​
/*
根据实际业务判断使用那套具体数据
A1 --> B1、B2、B3         3种
A2 --> B1、B2、B3         3种
A3 --> B1、B2、B3         3种
*/
- (void)fetch
{
  // 创建一个具体的ClassA
  _objA = [[ObjectA1 alloc] init];

  // 创建一个具体的ClassB
  BaseObjectB *b1 = [[ObjectB1 alloc] init];
  // 将一个具体的ClassB1 指定给抽象的ClassB
  _objA.objB = b1;

  // 获取数据
  [_objA handle];
}
@end

使用方中定义了ClassA对象,可以使用A1、A2、A3来创建不同的对象,获取不同的实现组合。BaseObjectB也可以有不同的实现组合。通过桥接模式不同的组合可以实现对象之间的解耦。
桥接模式的优点:

分离抽象接口及其实现部分。
桥接模式有时类似于多继承方案,但是多继承方案违背了类的单一职责原则(即一个类只有一个变化的原因),复用性比较差,而且多继承结构中类的个数非常庞大,桥接模式是比多继承方案更好的解决方法。
桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。
实现细节对客户透明,可以对用户隐藏实现细节

十、架构/框架

其中包括常见的如怎样设计图片缓存框架、网络框架,客户端的整体架构怎样实现,常见的解耦方式有哪些,多数同学都知道OPENURL是一种解耦方案,那依赖注入这种方式可能iOS的同学会感到陌生,这也是面试官期许的答案。

1、独立于APP的通用层(客户端最底层,放到任何APP当中都可以起到支撑作用) 时长统计框架、崩溃统计、网络的第三方库
2、通用业务层 当下公司业务相关某些通用的基础组件
3、中间层 对业务进行协调和解耦作用
4、各个业务线代码

十一、算法

有序数组归并、链表反转、字符串反转、大数相加算法思想等等,这部分变化就很多了。

十二、第三方

常见的AFNetworking、SDWebImageView、Reactive Cocoa、React Native等等

  1. AFURLSessionManager 最核心的类 2、AFHTTPSessionManager
    AFURLSessionManager的子类使用频度最高的类,其中包括AFURLRequestSerialzation:参数拼接成URLMapRequest,
  2. AFURLREsponseSerialzation:负责响应序列化,对请求返回结果解析
    3、NSURLSession:绘画模块;AFSecurityPolicy:用来保证网络安全,涉及到网络证书的校验,公钥的验证;AFNetworkReachabilityManager:对网络连接进行监听。在AFURLSessionManager中

常见问题:

  1. UI视图的事件传递机制是如何实现的?

  2. UI绘制原理是怎样的?

  3. 请利用TableView的重用机制实现一个字母索引条。

  4. 什么是离屏渲染?

  5. 什么是ARC? (可能有很多同学还不清楚ARC是编译器和Runtime的协作结果。很多同学在回答什么是ARC的时候往往会忽略掉Runtime在ARC当中所起到的重要作用。)

  6. AutoReleasePool的实现机制。(总结一句话:是以栈为结点构成的双向链表结构。)

  7. 循环引用相关的考察,NSTimer如果重复调用怎样解除循环引用?

  8. __block关键字是否可以解决循环引用? (其实__block属性关键字在MRC下是可以解决循环引用的,相信很多同学还不知道吧,哈哈。)

  9. Block的本质是什么? (好多同学会说Block的本质就是函数指针,殊不知这样的回答不准确、不完善;其实Block的本质是带有函数执行上下文环境的结构体,其中包含被调函数指针这么一个东西。)

  10. Block的截获变量的特性应该怎样解释,Block是怎样产生循环引用的?

  11. 怎样利用iOS的多线程技术对共享变量实现多读单写操作呢?

  12. 怎样理解自旋锁? 递归锁应该怎样使用?

  13. 常见的线程同步问题该怎样解决?

  14. 怎样解决DNS劫持?

  15. TCP的慢启动特点是怎样的。

  16. 你对HTTPS是怎样理解的?

  17. 给你一个实际场景,让大家现场提出利用哪个设计模式解决实际问题。

  18. 怎样设计一个时长统计框架?

  19. 怎样设计一个图片缓存框架?

  20. 客户端的整体架构实现是怎样的,解耦方式都有哪些?

  21. UIView和CALayer之间的关系是怎样的?请从设计原则的角度回答系统为何这样设计?

  22. UI卡顿、掉帧的原理是怎样的?

  23. 请解释一下你对isa指针的理解。

  24. 你是怎样理解引用计数机制的?(很多人会说什么retain\release\dealloc,完全没有Get到面试官的考察意图)

  25. 系统是怎样把一个weak变量添加到弱引用计数表中的?
    一个被声明为__weak的对象指针,经过编译器编译后会调用objc_initWeak(),storeWeak()这些函数调用栈,最终在weak_register_no_lock()这样的函数当中进行弱引用变量的添加:具体添加的位置是通过一个hash算法来进行位置查找,如果查找对应位置中已经有了当前对象所对应的弱引用数组,就会把新的弱引用变量添加到弱引用数组中;如果没有就会重新创建一个弱引用数组,然后把新的弱引用变量添加到第0个位置,后面的都初始化为nil。

  26. 请回答下面的代码输出结果:答案13

dispatch_async(global_queue, ^{
	NSLog(@”1”);
	[self performSelector : @selector(printLog) withObject : nil afterDelay : 0 ];
	NSLog(@”3”);
});
-(void)printLog {
	NSLog(@”2”);
}

输出1,3 分析: 异步方式分派到全局并发队列。这个block会在GCD底层维护的线程池中的某一个线程进行处理,这些线程默认是没有开启对应的runloop的;而performSelector即使是延迟0秒之后提交printLog任务,也是要创建一个提交printLog任务到runloop上的逻辑,所以在GCD底层创建的一个线程没有runloop的情况下performSelector方法会失效。 也就是说performSelector方法要有效执行,必须方法调用所属的线程有runloop,而当前GCD所创建的线程没有runloop,该方法就不会生效,所以不会被调用

27.下图函数打印结果是啥
IOS知识体系_第7张图片

你可能感兴趣的:(IOS)