最近准备把自己准备面试的一些知识都整理一下分享一下。内容主要针对面试会问到的常见问题进行分析。基本都是自己从不同地方总结来的。本文只是提供一个问题的方向以及比较简略的解答,具体还需要自己去查阅更多的资料。
多线程
关键词:区别 队列 用法
首先要知道,为什么要多线程?(腾讯)
使用多线程并不是为了加快速度提升性能的,多线程在CPU密集型的作业下的确不能提高性能甚至更浪费时间,但是在IO密集型的作业下则可以提升性能。
使用多线程的原因主要是通过异步调用避免阻塞,避免CPU空转。另外单CPU也可以多线程。
同时注意并行和并发的区别。
iOS中多线程的使用(腾讯)
1.NSThread
使用场景:一般用于常驻线程的创建,AFN2.x中就使用了其来创建一个常驻线程并添加runloop
2.NSOperationQueue
底层GCD实现,执行效率不如GCD。
可以设置优先级,最大并发数,默认-1。
可以设置依赖。
可以取消任务,观察任务状态(因为它封装成了对象)
使用场景:需要对任务实现精确控制。
3.GCD(前面两个一般就问问三者区别)
队列和线程的关系(网易)
同步 异步 串行队列 并发队列 这个排列组合自己去网上看结果。
同步和异步决定是否开辟新线程。
串行和并发决定开一条还是多条线程。
注意一下并发+同步,这时候队列中的任务是按顺序执行的(都没开新线程)
同步的话,block之后的任务是先执行的。
另外,特殊情况:主队列 同步 死锁,主队列 异步 block之后的任务会先执行!!主队列的任务虽然会加到主线程中执行,但是如果主线程里也有任务就必须等主线程任务执行完才轮到主队列的。
用GCD怎么实现依赖?(网易)
后面对其有依赖的必须要先执行的任务使用同步执行。(整理的时候发现的,当时我自己回答的时候的方法就不提了)
(本以为多线程这块会很多的,按照面试所涉及的问题整理发现这块好像确实内容不多)
Runtime
关键词:应用 消息
runtime应该是iOS面试中非常容易问到的一个点,面试官一般会问你你了解runtime吗,你就可以从他的功能出发,并且详细说说其应用。
runtime的应用?
1. method swizzling 俗称黑魔法
类方法和实例方法的方法交换有什么区别?(网易)
使用的函数不同 一个class_getClassMethod([类名 class] ,选择子) 获取元类 一个class_getInstanceMethod(self,选择子)里面直接传类
获取到IMP之后method_exchangeImplementations
为什么? (网易)
因为实例方法和类方法分别是存在类的方法和元类的方法列表里。类也是一个对象,那它也必须是另一个类的实列,这个类就是元类 (metaclass)。元类保存了类方法的列表。当一个类方法被调用时,元类会首先查找它本身是否有该类方法的实现,如果没有,则该元类会向它的父类查找该方法,直到一直找到继承链的头。
objc的runtime中,类是用objc_class结构体表示的,对象是用objc_object结构体表示的。 对象的isa用来标示这个对象是哪个类的实例。
SEL和IMP注意区分,SEL是方法编号,在一张表里和IMP对应,需要同时存在的意义可以理解为有时可以通过修改IMP的方式使一个SEL指向不同的函数指针。
有什么要注意的?(头条)
更换系统的方法时,要注意随着版本的替换,需要去进行重新的测试,因为系统的方法可能会进行了更新等等。
2. 自动归档和解档(头条)
如果一个模型有许多个属性,那么我们需要对每个属性都实现一遍encodeObject 和 decodeObjectForKey方法。
而使用class_copyIvarList获取类的全部成员变量,然后遍历即可。
第一个参数:表示获取哪个类中的成员变量
第二个参数:表示这个类有多少成员变量,传入一个Int变量地址,会自动给这个变量赋值
返回值Ivar *:指的是一个ivar数组,会把所有成员属性放在一个数组中,通过返回的数组就能全部获取到。
然后通过KVC获取相应的value,进行归档和解档
3.字典转模型 同理
4.给类动态添加属性应用场景:给系统的类添加属性的时候
因为分类中不能直接添加属性,所以要通过设置关联来实现。
objc_setAssociatedObject和objc_setAssociatedObject
5.动态添加方法
通过performSelector调用,在消息转发的resolveInstanceMethod中添加方法实现class_addMethod
为了适应面试需要,经过了一些筛选和精炼,如果其中有不懂的可以查阅 具体参考
引用计数相关
关键词:ARC,weak,@autorelease,循环引用(请参阅相关文章对其进行深入了解)
ARC是什么?(网易)
ARC 自动引用计数automatic reference counting,是指对象拥有者被释放后,该对象所占用的内存也会被释放,相对于MRC manual reference counting 手动引用计数 ,ARC只是在编译的时候根据代码上下文帮你自动加入了retain release autorelease等,真正的释放内存是objc本身的retainCount机制自带的。
weak的对象是怎么释放的?(网易)
对于weak 对象会放入一个 hash 表中。 用 weak 指向的对象内存地址作为 key,当此对象的引用计数为0的时候会 dealloc,假如 weak 指向的对象内存地址是a,那么就会以a为键, 在这个 weak 表中搜索,找到所有以a为键的 weak 对象,从而设置为 nil。
你知道@autoreleasepool吗?(网易)
runloop的每一次循环是被@autoreleasepool包裹的,它实际上就是在{}两侧帮你加上objc_autoreleasePoolPush()和pop,pop之后会对其中的元素进行release,从而实现手动干预。
在@autoreleasepool中创建的变量,会在@autoreleasepool结束的时候执行一次release,进行释放。其实@autoreleasepool就相当于一层作用域。
每一个线程创建的时候就会有一个autorelease pool的创建,并且在线程退出的时候,清空整个autorelease pool。
@autoreleasepool作用(头条、网易)
一般用来控制内存峰值,比如一个大次数循环里。还有一些命令行程序,没有UI框架。还有一些长时间在后台的任务。
引用循环(网易)
block、delegate(前两个可以通过weak来防止循环引用)、NSTimer(需要invalidate停止之后才能释放,置为nil)
__bridge(网易)
Core Foundation对象简称CF,例如Core Graphics、Core Text等。
ARC环境下编译器不会自动管理CF对象的内存,所以当我们创建了一个CF对象以后就需要我们使用CFRelease将其手动释放。
有时需要将CoreFoundation(CF) 框架的对象和 OC 对象之间的类型转换,这时候我们需要 __bridge 来转换(只转换类型,还是要CF自己释放)。
如果用_bridge_transfer和_bridge_retained(相反)就可以把对象的所有权转移,ARC就可以管理了 (transfer)
block(网易)
需要了解一下block的内部实现,内部有一个isa指针指向Class对象,invoke指向实现代码,加了__block和不加的变量,内部捕获的一个是指针一个是拷贝。
还要注意一下不是所有block都会引起强引用循环的,有些block,比如动画的,一些第三方库的等等,是系统持有的这个block而不是self,这种情况block内部是可以直接使用self的。
Runloop
关键词:Mode 生命周期 Source
必考 真的是必考
实现?(头条)
其实就是类似while循环。
RunloopMode(网易)
run loopmode主要是用来指定事件在运行循环中的优先级的,分为:优先级!!
NSDefaultRunLoopMode(kCFRunLoopDefaultMode):默认,空闲状态
UITrackingRunLoopMode:ScrollView滑动时
UIInitializationRunLoopMode:启动时
NSRunLoopCommonModes(kCFRunLoopCommonModes):Mode集合
苹果公开提供的Mode 有两个:
NSDefaultRunLoopMode(kCFRunLoopDefaultMode)
NSRunLoopCommonModes(kCFRunLoopCommonModes)
这个我是结合NSTimer和TableView来说的,描述一个defaultmode下的Timer在滑动时(进入UITrackingRunLoopMode)时无法接收到回调的场景(runloop只能运行在一种mode下),此时只需要把NSTimer加入到NSRunLoopCommonModes中即可。
流程(网易)
runloopsource0指的是非基于端口port,说白了也就是处理触摸事件,selector事件,source1指的是基于端口的port:是处理系统的一些事件。
runloop和线程的关系(网易)
总的说来,Run loop,正如其名,loop表示某种循环,和run放在一起就表示一直在运行着的循环。实际上,run loop和线程是紧密相连的,可以这样说run loop是为了线程而生,没有线程,它就没有存在的必要。Run loops是线程的基础架构部分,Cocoa 和CoreFundation 都提供了run loop 对象方便配置和管理线程的run loop(以下都以Cocoa 为例)。每个线程,包括程序的主线程(main thread )都有与之相应的run loop 对象。
两者是一一对应的!他们的关系保存在一个全局的字典里
runloop是来管理线程的,当线程的runloop被开启后,线程会在执行完任务后进入休眠状态,有了任务就会被唤醒去执行任务。只有停止了runloop才能销毁线程
如果程序中,需要经常在子线程中执行任务,频繁的创建和销毁线程,会造成资源的浪费。
这时候我们就可以使用RunLoop来让该线程长时间存活而不被销毁。
锁
好像不怎么考,但是还是要了解一下,了解一下各个锁的原理区别
各种锁性能注意其中的OSSpinLock已经被标记为 Deprecated
缓存策略
比较常考,头条和网易都问了,FIFO和LRU就不细说了,这几个都需要知道他们的实现方式。
LRU-K
LRU-K中的K代表最近使用的次数,因此LRU可以认为是LRU-1。LRU-K的主要目的是为了解决LRU算法“缓存污染”的问题,其核心思想是将“最近使用过1次”的判断标准扩展为“最近使用过K次”。
相比LRU,LRU-K需要多维护一个队列,用于记录所有缓存数据被访问的历史。只有当数据的访问次数达到K次的时候,才将数据放入缓存。当需要淘汰数据时,LRU-K会淘汰第K次访问时间距当前时间最大的数据。
数据第一次被访问时,加入到历史访问列表,如果书籍在访问历史列表中没有达到K次访问,则按照一定的规则(FIFO,LRU)淘汰;当访问历史队列中的数据访问次数达到K次后,将数据索引从历史队列中删除,将数据移到缓存队列中,并缓存数据,缓存队列重新按照时间排序;缓存数据队列中被再次访问后,重新排序,需要淘汰数据时,淘汰缓存队列中排在末尾的数据,即“淘汰倒数K次访问离现在最久的数据”。
LRU-K具有LRU的优点,同时还能避免LRU的缺点,实际应用中LRU-2是综合最优的选择。由于LRU-K还需要记录那些被访问过、但还没有放入缓存的对象,因此内存消耗会比LRU要多。
2Q
Two queues(以下使用2Q代替)算法类似于LRU-2,不同点在于2Q将LRU-2算法中的访问历史队列(注意这不是缓存数据的)改为一个FIFO缓存队列,即:2Q算法有两个缓存队列,一个是FIFO队列,一个是LRU队列。当数据第一次访问时,2Q算法将数据缓存在FIFO队列里面,当数据第二次被访问时,则将数据从FIFO队列移到LRU队列里面,两个队列各自按照自己的方法淘汰数据。
新访问的数据插入到FIFO队列中,如果数据在FIFO队列中一直没有被再次访问,则最终按照FIFO规则淘汰;如果数据在FIFO队列中再次被访问到,则将数据移到LRU队列头部,如果数据在LRU队列中再次被访问,则将数据移动LRU队列头部,LRU队列淘汰末尾的数据。类似的还有MQ。
待更
查看本人总结的头条、腾讯、网易、阿里面试题
这段时间会继续总结一些面试知识,内容只针对于面试常问的。
网友评论